From cb51858dbffc1040a7b26caa4d83d0d2a2d2924e Mon Sep 17 00:00:00 2001 From: Dominik Czupryna Date: Tue, 3 Oct 2023 14:26:11 +0200 Subject: [PATCH 1/8] chore: update android pemissions docs --- CHANGELOG.md | 6 ++++++ INTRO.md | 14 ++++++++++++++ README.md | 19 ++++++++++++++++++- android/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 5 ++--- example/src/services/BLEService/BLEService.ts | 6 ++---- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5792d500..c8cd8c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [3.0.1] - 2023-10-03 + +### Changed + +- Android permissions section in docs and readme + ## [3.0.0] - 2023-09-28 ### Added diff --git a/INTRO.md b/INTRO.md index 5b1c71cc..1bf80a1c 100644 --- a/INTRO.md +++ b/INTRO.md @@ -68,6 +68,20 @@ requestBluetoothPermission = async () => { } ``` +With `neverForLocation` flag active, you can remove `ACCESS_FINE_LOCATION` permissions ask e.g.: + +```js +const result = await PermissionsAndroid.requestMultiple([ + PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, + PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT +]) + +return ( + result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED && + result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED +) +``` + ## Waiting for Powered On state When iOS application launches BLE stack is not immediately available and we need to check its status. diff --git a/README.md b/README.md index e0dc4652..1eb2e11d 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Contact us at [intent](https://withintent.com/contact-us/?utm_source=github&utm_ } ``` -1. (Optional) In `AndroidManifest.xml`, add Bluetooth permissions and update ``: +1. In `AndroidManifest.xml`, add Bluetooth permissions and update ``: ```xml @@ -144,4 +144,21 @@ Contact us at [intent](https://withintent.com/contact-us/?utm_source=github&utm_ ... ``` +1. (Optional) In SDK 31+ You can remove `ACCESS_FINE_LOCATION` (or mark it as `android:maxSdkVersion="30"` ) from `AndroidManifest.xml` and add `neverForLocation` flag into `BLUETOOTH_SCAN` permissions which says that you will not use location based on scanning eg: + + ```xml + + + + + + + + + + ... + ``` + + With `neverForLocation` flag active, you no longer need to ask for `ACCESS_FINE_LOCATION` in your app + ## Troubleshooting diff --git a/android/build.gradle b/android/build.gradle index b17854f2..c0b30534 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -90,7 +90,7 @@ dependencies { // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" - implementation 'com.github.dotintent:MultiPlatformBleAdapter:0.2.0' + implementation 'com.github.dotintent:MultiPlatformBleAdapter:a136c3f4ac' } if (isNewArchitectureEnabled()) { diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index b029d1b0..0e144786 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -2,13 +2,12 @@ - + - - + diff --git a/example/src/services/BLEService/BLEService.ts b/example/src/services/BLEService/BLEService.ts index 2a22102b..6775c705 100644 --- a/example/src/services/BLEService/BLEService.ts +++ b/example/src/services/BLEService/BLEService.ts @@ -393,14 +393,12 @@ class BLEServiceInstance { if (PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN && PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT) { const result = await PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, - PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, - PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION + PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT ]) return ( result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED && - result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED && - result['android.permission.ACCESS_FINE_LOCATION'] === PermissionsAndroid.RESULTS.GRANTED + result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED ) } } From 33d1b300758c113d8eff9876370fd34614a48244 Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski Date: Mon, 9 Oct 2023 12:29:29 +0200 Subject: [PATCH 2/8] refactor: move everything from MultiplatformAdapter library into android library folder --- android/build.gradle | 4 +- android/src/main/AndroidManifest.xml | 19 +- .../main/java/com/bleplx/BlePlxModule.java | 30 +- .../com/bleplx/adapter/AdvertisementData.java | 250 +++ .../java/com/bleplx/adapter/BleAdapter.java | 250 +++ .../com/bleplx/adapter/BleAdapterCreator.java | 7 + .../com/bleplx/adapter/BleAdapterFactory.java | 21 + .../java/com/bleplx/adapter/BleException.java | 4 + .../java/com/bleplx/adapter/BleModule.java | 1565 +++++++++++++++++ .../com/bleplx/adapter/Characteristic.java | 151 ++ .../com/bleplx/adapter/ConnectionOptions.java | 72 + .../com/bleplx/adapter/ConnectionState.java | 12 + .../java/com/bleplx/adapter/Descriptor.java | 115 ++ .../main/java/com/bleplx/adapter/Device.java | 81 + .../com/bleplx/adapter/OnErrorCallback.java | 8 + .../com/bleplx/adapter/OnEventCallback.java | 6 + .../com/bleplx/adapter/OnSuccessCallback.java | 6 + .../com/bleplx/adapter/RefreshGattMoment.java | 20 + .../java/com/bleplx/adapter/ScanResult.java | 132 ++ .../main/java/com/bleplx/adapter/Service.java | 56 + .../com/bleplx/adapter/errors/BleError.java | 32 + .../bleplx/adapter/errors/BleErrorCode.java | 57 + .../bleplx/adapter/errors/BleErrorUtils.java | 86 + .../bleplx/adapter/errors/ErrorConverter.java | 219 +++ .../CannotMonitorCharacteristicException.java | 15 + .../bleplx/adapter/utils/Base64Converter.java | 12 + .../com/bleplx/adapter/utils/ByteUtils.java | 15 + .../com/bleplx/adapter/utils/Constants.java | 66 + .../bleplx/adapter/utils/DisposableMap.java | 39 + .../com/bleplx/adapter/utils/IdGenerator.java | 22 + .../bleplx/adapter/utils/IdGeneratorKey.java | 37 + .../com/bleplx/adapter/utils/LogLevel.java | 47 + .../utils/RefreshGattCustomOperation.java | 48 + .../bleplx/adapter/utils/SafeExecutor.java | 33 + .../bleplx/adapter/utils/ServiceFactory.java | 16 + .../bleplx/adapter/utils/UUIDConverter.java | 51 + .../mapper/RxBleDeviceToDeviceMapper.java | 19 + .../RxScanResultToScanResultMapper.java | 20 + .../BleErrorToJsObjectConverter.java | 4 +- .../CharacteristicToJsObjectConverter.java | 6 +- .../DescriptorToJsObjectConverter.java | 8 +- .../converter/DeviceToJsObjectConverter.java | 4 +- .../ScanResultToJsObjectConverter.java | 8 +- .../converter/ServiceToJsObjectConverter.java | 4 +- 44 files changed, 3642 insertions(+), 35 deletions(-) create mode 100644 android/src/main/java/com/bleplx/adapter/AdvertisementData.java create mode 100644 android/src/main/java/com/bleplx/adapter/BleAdapter.java create mode 100644 android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java create mode 100644 android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java create mode 100644 android/src/main/java/com/bleplx/adapter/BleException.java create mode 100755 android/src/main/java/com/bleplx/adapter/BleModule.java create mode 100755 android/src/main/java/com/bleplx/adapter/Characteristic.java create mode 100644 android/src/main/java/com/bleplx/adapter/ConnectionOptions.java create mode 100644 android/src/main/java/com/bleplx/adapter/ConnectionState.java create mode 100644 android/src/main/java/com/bleplx/adapter/Descriptor.java create mode 100755 android/src/main/java/com/bleplx/adapter/Device.java create mode 100644 android/src/main/java/com/bleplx/adapter/OnErrorCallback.java create mode 100644 android/src/main/java/com/bleplx/adapter/OnEventCallback.java create mode 100644 android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java create mode 100755 android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java create mode 100644 android/src/main/java/com/bleplx/adapter/ScanResult.java create mode 100755 android/src/main/java/com/bleplx/adapter/Service.java create mode 100755 android/src/main/java/com/bleplx/adapter/errors/BleError.java create mode 100755 android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java create mode 100755 android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java create mode 100755 android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java create mode 100755 android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java create mode 100644 android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/Constants.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/LogLevel.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java create mode 100644 android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java create mode 100644 android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java create mode 100755 android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java create mode 100644 android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java create mode 100644 android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java diff --git a/android/build.gradle b/android/build.gradle index c0b30534..54b42751 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -81,7 +81,6 @@ android { repositories { mavenCentral() google() - maven { url 'https://jitpack.io' } } @@ -90,7 +89,8 @@ dependencies { // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" - implementation 'com.github.dotintent:MultiPlatformBleAdapter:a136c3f4ac' + implementation 'io.reactivex.rxjava2:rxjava:2.2.17' + implementation "com.polidea.rxandroidble2:rxandroidble:1.17.2" } if (isNewArchitectureEnabled()) { diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index ab962717..8854d00e 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,20 @@ + package="com.bleplx" + xmlns:tools="http://schemas.android.com/tools"> + + + + + + + diff --git a/android/src/main/java/com/bleplx/BlePlxModule.java b/android/src/main/java/com/bleplx/BlePlxModule.java index 229a7023..1464bd27 100644 --- a/android/src/main/java/com/bleplx/BlePlxModule.java +++ b/android/src/main/java/com/bleplx/BlePlxModule.java @@ -14,20 +14,20 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.polidea.multiplatformbleadapter.BleAdapter; -import com.polidea.multiplatformbleadapter.BleAdapterFactory; -import com.polidea.multiplatformbleadapter.Characteristic; -import com.polidea.multiplatformbleadapter.ConnectionOptions; -import com.polidea.multiplatformbleadapter.ConnectionState; -import com.polidea.multiplatformbleadapter.Descriptor; -import com.polidea.multiplatformbleadapter.Device; -import com.polidea.multiplatformbleadapter.OnErrorCallback; -import com.polidea.multiplatformbleadapter.OnEventCallback; -import com.polidea.multiplatformbleadapter.OnSuccessCallback; -import com.polidea.multiplatformbleadapter.RefreshGattMoment; -import com.polidea.multiplatformbleadapter.ScanResult; -import com.polidea.multiplatformbleadapter.Service; -import com.polidea.multiplatformbleadapter.errors.BleError; +import com.bleplx.adapter.BleAdapter; +import com.bleplx.adapter.BleAdapterFactory; +import com.bleplx.adapter.Characteristic; +import com.bleplx.adapter.ConnectionOptions; +import com.bleplx.adapter.ConnectionState; +import com.bleplx.adapter.Descriptor; +import com.bleplx.adapter.Device; +import com.bleplx.adapter.OnErrorCallback; +import com.bleplx.adapter.OnEventCallback; +import com.bleplx.adapter.OnSuccessCallback; +import com.bleplx.adapter.RefreshGattMoment; +import com.bleplx.adapter.ScanResult; +import com.bleplx.adapter.Service; +import com.bleplx.adapter.errors.BleError; import com.bleplx.converter.BleErrorToJsObjectConverter; import com.bleplx.converter.CharacteristicToJsObjectConverter; import com.bleplx.converter.DescriptorToJsObjectConverter; @@ -936,7 +936,7 @@ public void onError(BleError bleError) { @ReactMethod public void addListener(String eventName) { - // Keep: Required for RN built in Event Emitter Calls. + // Keep: Required for RN built in Event Emitter Calls. } @ReactMethod diff --git a/android/src/main/java/com/bleplx/adapter/AdvertisementData.java b/android/src/main/java/com/bleplx/adapter/AdvertisementData.java new file mode 100644 index 00000000..35fbb3b1 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/AdvertisementData.java @@ -0,0 +1,250 @@ +package com.bleplx.adapter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +public class AdvertisementData { + + private byte[] manufacturerData; + private Map serviceData; + private List serviceUUIDs; + private String localName; + private Integer txPowerLevel; + private List solicitedServiceUUIDs; + private byte[] rawScanRecord; + + private static final long BLUETOOTH_BASE_UUID_LSB = 0x800000805F9B34FBL; + private static final int BLUETOOTH_BASE_UUID_MSB = 0x00001000; + + public String getLocalName() { + return localName; + } + + public byte[] getManufacturerData() { + return manufacturerData; + } + + public Map getServiceData() { + return serviceData; + } + + public List getServiceUUIDs() { + return serviceUUIDs; + } + + public Integer getTxPowerLevel() { + return txPowerLevel; + } + + public List getSolicitedServiceUUIDs() { + return solicitedServiceUUIDs; + } + + public byte[] getRawScanRecord() { + return rawScanRecord; + } + + private AdvertisementData() {} + + public AdvertisementData(byte[] manufacturerData, + Map serviceData, + List serviceUUIDs, + String localName, + Integer txPowerLevel, + List solicitedServiceUUIDs) { + this.manufacturerData = manufacturerData; + this.serviceData = serviceData; + this.serviceUUIDs = serviceUUIDs; + this.localName = localName; + this.txPowerLevel = txPowerLevel; + this.solicitedServiceUUIDs = solicitedServiceUUIDs; + } + + public static AdvertisementData parseScanResponseData(byte[] advertisement) { + AdvertisementData advData = new AdvertisementData(); + advData.rawScanRecord = advertisement; + + ByteBuffer rawData = ByteBuffer.wrap(advertisement).order(ByteOrder.LITTLE_ENDIAN); + while (rawData.remaining() >= 2) { + int adLength = rawData.get() & 0xFF; + if (adLength == 0) break; + adLength -= 1; + int adType = rawData.get() & 0xFF; + if (rawData.remaining() < adLength) break; + parseAdvertisementData(advData, adType, adLength, rawData.slice().order(ByteOrder.LITTLE_ENDIAN)); + rawData.position(rawData.position() + adLength); + } + return advData; + } + + private static void parseAdvertisementData(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { + switch (adType) { + case 0xFF: + parseManufacturerData(advData, adLength, data); + break; + + case 0x02: + case 0x03: + parseServiceUUIDs(advData, adLength, data, 2); + break; + case 0x04: + case 0x05: + parseServiceUUIDs(advData, adLength, data, 4); + break; + case 0x06: + case 0x07: + parseServiceUUIDs(advData, adLength, data, 16); + break; + + case 0x08: + case 0x09: + parseLocalName(advData, adType, adLength, data); + break; + + case 0x0A: + parseTxPowerLevel(advData, adLength, data); + break; + + case 0x14: + parseSolicitedServiceUUIDs(advData, adLength, data, 2); + break; + case 0x1F: + parseSolicitedServiceUUIDs(advData, adLength, data, 4); + break; + case 0x15: + parseSolicitedServiceUUIDs(advData, adLength, data, 16); + break; + + case 0x16: + parseServiceData(advData, adLength, data, 2); + break; + case 0x20: + parseServiceData(advData, adLength, data, 4); + break; + case 0x21: + parseServiceData(advData, adLength, data, 16); + break; + } + } + + private static void parseLocalName(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { + // Complete local name is preferred over short local name. + if (advData.localName == null || adType == 0x09) { + byte[] bytes = new byte[adLength]; + data.get(bytes, 0, adLength); + advData.localName = new String(bytes, Charset.forName("UTF-8")); + } + } + + private static UUID parseUUID(ByteBuffer data, int uuidLength) { + long lsb; + long msb; + switch (uuidLength) { + case 2: + msb = (((long) data.getShort() & 0xFFFF) << 32) + BLUETOOTH_BASE_UUID_MSB; + lsb = BLUETOOTH_BASE_UUID_LSB; + break; + case 4: + msb = ((long) data.getInt() << 32) + BLUETOOTH_BASE_UUID_MSB; + lsb = BLUETOOTH_BASE_UUID_LSB; + break; + case 16: + lsb = data.getLong(); + msb = data.getLong(); + break; + default: + data.position(data.position() + uuidLength); + return null; + } + return new UUID(msb, lsb); + } + + private static void parseSolicitedServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (advData.solicitedServiceUUIDs == null) advData.solicitedServiceUUIDs = new ArrayList<>(); + while (data.remaining() >= uuidLength && data.position() < adLength) { + advData.solicitedServiceUUIDs.add(parseUUID(data, uuidLength)); + } + } + + private static void parseServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (advData.serviceUUIDs == null) advData.serviceUUIDs = new ArrayList<>(); + while (data.remaining() >= uuidLength && data.position() < adLength) { + advData.serviceUUIDs.add(parseUUID(data, uuidLength)); + } + } + + private static void parseServiceData(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (adLength < uuidLength) return; + if (advData.serviceData == null) advData.serviceData = new HashMap<>(); + UUID serviceUUID = parseUUID(data, uuidLength); + int serviceDataLength = adLength - uuidLength; + byte[] serviceData = new byte[serviceDataLength]; + data.get(serviceData, 0, serviceDataLength); + advData.serviceData.put(serviceUUID, serviceData); + } + + private static void parseTxPowerLevel(AdvertisementData advData, int adLength, ByteBuffer data) { + if (adLength != 1) return; + advData.txPowerLevel = (int) data.get(); + } + + private static void parseManufacturerData(AdvertisementData advData, int adLength, ByteBuffer data) { + if (adLength < 2) return; + advData.manufacturerData = new byte[adLength]; + data.get(advData.manufacturerData, 0, adLength); + } + + @Override + public String toString() { + return "AdvertisementData{" + + "manufacturerData=" + Arrays.toString(manufacturerData) + + ", serviceData=" + serviceData + + ", serviceUUIDs=" + serviceUUIDs + + ", localName='" + localName + '\'' + + ", txPowerLevel=" + txPowerLevel + + ", solicitedServiceUUIDs=" + solicitedServiceUUIDs + + ", rawScanRecord=" + Arrays.toString(rawScanRecord) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AdvertisementData that = (AdvertisementData) o; + + if (!Arrays.equals(manufacturerData, that.manufacturerData)) return false; + if (!Objects.equals(serviceData, that.serviceData)) + return false; + if (!Objects.equals(serviceUUIDs, that.serviceUUIDs)) + return false; + if (!Objects.equals(localName, that.localName)) + return false; + if (!Objects.equals(txPowerLevel, that.txPowerLevel)) + return false; + if (!Objects.equals(solicitedServiceUUIDs, that.solicitedServiceUUIDs)) + return false; + return Arrays.equals(rawScanRecord, that.rawScanRecord); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(manufacturerData); + result = 31 * result + (serviceData != null ? serviceData.hashCode() : 0); + result = 31 * result + (serviceUUIDs != null ? serviceUUIDs.hashCode() : 0); + result = 31 * result + (localName != null ? localName.hashCode() : 0); + result = 31 * result + (txPowerLevel != null ? txPowerLevel.hashCode() : 0); + result = 31 * result + (solicitedServiceUUIDs != null ? solicitedServiceUUIDs.hashCode() : 0); + result = 31 * result + Arrays.hashCode(rawScanRecord); + return result; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapter.java b/android/src/main/java/com/bleplx/adapter/BleAdapter.java new file mode 100644 index 00000000..9d63f3bd --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/BleAdapter.java @@ -0,0 +1,250 @@ +package com.bleplx.adapter; + +import com.bleplx.adapter.errors.BleError; + +import java.util.List; + +public interface BleAdapter { + + void createClient(String restoreStateIdentifier, + OnEventCallback onAdapterStateChangeCallback, + OnEventCallback onStateRestored); + + void destroyClient(); + + void enable( + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void disable( + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + String getCurrentState(); + + void startDeviceScan( + String[] filteredUUIDs, + int scanMode, + int callbackType, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void stopDeviceScan(); + + void requestConnectionPriorityForDevice( + String deviceIdentifier, + int connectionPriority, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readRSSIForDevice( + String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void requestMTUForDevice( + String deviceIdentifier, + int mtu, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void getKnownDevices( + String[] deviceIdentifiers, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void getConnectedDevices( + String[] serviceUUIDs, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void connectToDevice( + String deviceIdentifier, + ConnectionOptions connectionOptions, + OnSuccessCallback onSuccessCallback, + OnEventCallback onConnectionStateChangedCallback, + OnErrorCallback onErrorCallback); + + void cancelDeviceConnection( + String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void isDeviceConnected( + String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void discoverAllServicesAndCharacteristicsForDevice( + String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + List getServicesForDevice( + String deviceIdentifier) throws BleError; + + List getCharacteristicsForDevice( + String deviceIdentifier, + String serviceUUID) throws BleError; + + List getCharacteristicsForService( + int serviceIdentifier) throws BleError; + + List descriptorsForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID) throws BleError; + + List descriptorsForService( + int serviceIdentifier, + String characteristicUUID) throws BleError; + + List descriptorsForCharacteristic( + int characteristicIdentifier) throws BleError; + + + void readCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readCharacteristic( + int characteristicIdentifer, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristic( + int characteristicIdentifier, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristic( + int characteristicIdentifier, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void readDescriptorForDevice( + final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptorForService( + final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptorForCharacteristic( + final int characteristicIdentifier, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptor( + final int descriptorIdentifier, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeDescriptorForDevice( + final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptorForService( + final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptorForCharacteristic( + final int characteristicIdentifier, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptor( + final int descriptorIdentifier, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void cancelTransaction(String transactionId); + + void setLogLevel(String logLevel); + + String getLogLevel(); +} diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java b/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java new file mode 100644 index 00000000..1d9fb361 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java @@ -0,0 +1,7 @@ +package com.bleplx.adapter; + +import android.content.Context; + +public interface BleAdapterCreator { + BleAdapter createAdapter(Context context); +} diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java b/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java new file mode 100644 index 00000000..b2143895 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java @@ -0,0 +1,21 @@ +package com.bleplx.adapter; + +import android.content.Context; + +public class BleAdapterFactory { + + private static BleAdapterCreator bleAdapterCreator = new BleAdapterCreator() { + @Override + public BleAdapter createAdapter(Context context) { + return new BleModule(context); + } + }; + + public static BleAdapter getNewAdapter(Context context) { + return bleAdapterCreator.createAdapter(context); + } + + public static void setBleAdapterCreator(BleAdapterCreator bleAdapterCreator) { + BleAdapterFactory.bleAdapterCreator = bleAdapterCreator; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/BleException.java b/android/src/main/java/com/bleplx/adapter/BleException.java new file mode 100644 index 00000000..3f470b32 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/BleException.java @@ -0,0 +1,4 @@ +package com.bleplx.adapter; + +public interface BleException { +} diff --git a/android/src/main/java/com/bleplx/adapter/BleModule.java b/android/src/main/java/com/bleplx/adapter/BleModule.java new file mode 100755 index 00000000..4c2cf8f5 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/BleModule.java @@ -0,0 +1,1565 @@ +package com.bleplx.adapter; + +import static com.bleplx.adapter.utils.Constants.BluetoothState; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.ParcelUuid; +import android.util.SparseArray; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bleplx.adapter.errors.BleError; +import com.bleplx.adapter.errors.BleErrorCode; +import com.bleplx.adapter.errors.BleErrorUtils; +import com.bleplx.adapter.errors.ErrorConverter; +import com.bleplx.adapter.exceptions.CannotMonitorCharacteristicException; +import com.bleplx.adapter.utils.Base64Converter; +import com.bleplx.adapter.utils.Constants; +import com.bleplx.adapter.utils.DisposableMap; +import com.bleplx.adapter.utils.IdGenerator; +import com.bleplx.adapter.utils.LogLevel; +import com.bleplx.adapter.utils.RefreshGattCustomOperation; +import com.bleplx.adapter.utils.SafeExecutor; +import com.bleplx.adapter.utils.ServiceFactory; +import com.bleplx.adapter.utils.UUIDConverter; +import com.bleplx.adapter.utils.mapper.RxBleDeviceToDeviceMapper; +import com.bleplx.adapter.utils.mapper.RxScanResultToScanResultMapper; +import com.polidea.rxandroidble2.NotificationSetupMode; +import com.polidea.rxandroidble2.RxBleAdapterStateObservable; +import com.polidea.rxandroidble2.RxBleClient; +import com.polidea.rxandroidble2.RxBleConnection; +import com.polidea.rxandroidble2.RxBleDevice; +import com.polidea.rxandroidble2.internal.RxBleLog; +import com.polidea.rxandroidble2.scan.ScanFilter; +import com.polidea.rxandroidble2.scan.ScanSettings; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Action; +import io.reactivex.schedulers.Schedulers; + +public class BleModule implements BleAdapter { + + private final ErrorConverter errorConverter = new ErrorConverter(); + + @Nullable + private RxBleClient rxBleClient; + + private final HashMap discoveredDevices = new HashMap<>(); + + private final HashMap connectedDevices = new HashMap<>(); + + private final HashMap activeConnections = new HashMap<>(); + + private final SparseArray discoveredServices = new SparseArray<>(); + + private final SparseArray discoveredCharacteristics = new SparseArray<>(); + + private final SparseArray discoveredDescriptors = new SparseArray<>(); + + private final DisposableMap pendingTransactions = new DisposableMap(); + + private final DisposableMap connectingDevices = new DisposableMap(); + + private final BluetoothManager bluetoothManager; + + private final BluetoothAdapter bluetoothAdapter; + + private final Context context; + + @Nullable + private Disposable scanSubscription; + + @Nullable + private Disposable adapterStateChangesSubscription; + + private final RxBleDeviceToDeviceMapper rxBleDeviceToDeviceMapper = new RxBleDeviceToDeviceMapper(); + + private final RxScanResultToScanResultMapper rxScanResultToScanResultMapper = new RxScanResultToScanResultMapper(); + + private final ServiceFactory serviceFactory = new ServiceFactory(); + + private int currentLogLevel = RxBleLog.NONE; + + public BleModule(Context context) { + this.context = context; + bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + bluetoothAdapter = bluetoothManager.getAdapter(); + } + + @Override + public void createClient(String restoreStateIdentifier, + OnEventCallback onAdapterStateChangeCallback, + OnEventCallback onStateRestored) { + rxBleClient = RxBleClient.create(context); + adapterStateChangesSubscription = monitorAdapterStateChanges(context, onAdapterStateChangeCallback); + + // We need to send signal that BLE Module starts without restored state + if (restoreStateIdentifier != null) { + onStateRestored.onEvent(null); + } + } + + @Override + public void destroyClient() { + if (adapterStateChangesSubscription != null) { + adapterStateChangesSubscription.dispose(); + adapterStateChangesSubscription = null; + } + if (scanSubscription != null && !scanSubscription.isDisposed()) { + scanSubscription.dispose(); + scanSubscription = null; + } + pendingTransactions.removeAllSubscriptions(); + connectingDevices.removeAllSubscriptions(); + + discoveredServices.clear(); + discoveredCharacteristics.clear(); + discoveredDescriptors.clear(); + connectedDevices.clear(); + activeConnections.clear(); + discoveredDevices.clear(); + + rxBleClient = null; + IdGenerator.clear(); + } + + + @Override + public void enable(final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + changeAdapterState( + RxBleAdapterStateObservable.BleAdapterState.STATE_ON, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void disable(final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + changeAdapterState( + RxBleAdapterStateObservable.BleAdapterState.STATE_OFF, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @BluetoothState + @Override + public String getCurrentState() { + if (!supportsBluetoothLowEnergy()) return BluetoothState.UNSUPPORTED; + if (bluetoothManager == null) return BluetoothState.POWERED_OFF; + return mapNativeAdapterStateToLocalBluetoothState(bluetoothAdapter.getState()); + } + + @Override + public void startDeviceScan(String[] filteredUUIDs, + int scanMode, + int callbackType, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + UUID[] uuids = null; + + if (filteredUUIDs != null) { + uuids = UUIDConverter.convert(filteredUUIDs); + if (uuids == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(filteredUUIDs)); + return; + } + } + + safeStartDeviceScan(uuids, scanMode, callbackType, onEventCallback, onErrorCallback); + } + + @Override + public void stopDeviceScan() { + if (scanSubscription != null) { + scanSubscription.dispose(); + scanSubscription = null; + } + } + + @Override + public void requestConnectionPriorityForDevice(String deviceIdentifier, + int connectionPriority, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } + + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }).subscribe((Action) () -> { + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Override + public void readRSSIForDevice(String deviceIdentifier, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .readRssi() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(rssi -> { + device.setRssi(rssi); + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Override + public void requestMTUForDevice(String deviceIdentifier, int mtu, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } + + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .requestMtu(mtu) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }).subscribe(outputMtu -> { + device.setMtu(outputMtu); + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Override + public void getKnownDevices(String[] deviceIdentifiers, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get known devices", null)); + return; + } + + List knownDevices = new ArrayList<>(); + for (final String deviceId : deviceIdentifiers) { + if (deviceId == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(deviceIdentifiers)); + return; + } + + final Device device = discoveredDevices.get(deviceId); + if (device != null) { + knownDevices.add(device); + } + } + + onSuccessCallback.onSuccess(knownDevices.toArray(new Device[knownDevices.size()])); + } + + @Override + public void getConnectedDevices(String[] serviceUUIDs, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get connected devices", null)); + return; + } + + if (serviceUUIDs.length == 0) { + onSuccessCallback.onSuccess(new Device[0]); + return; + } + + UUID[] uuids = new UUID[serviceUUIDs.length]; + for (int i = 0; i < serviceUUIDs.length; i++) { + UUID uuid = UUIDConverter.convert(serviceUUIDs[i]); + + if (uuid == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUIDs)); + return; + } + + uuids[i] = uuid; + } + + List localConnectedDevices = new ArrayList<>(); + for (Device device : connectedDevices.values()) { + for (UUID uuid : uuids) { + if (device.getServiceByUUID(uuid) != null) { + localConnectedDevices.add(device); + break; + } + } + } + + onSuccessCallback.onSuccess(localConnectedDevices.toArray(new Device[localConnectedDevices.size()])); + + } + + @Override + public void connectToDevice(String deviceIdentifier, + ConnectionOptions connectionOptions, + OnSuccessCallback onSuccessCallback, + OnEventCallback onConnectionStateChangedCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to connect to device", null)); + return; + } + + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + return; + } + + safeConnectToDevice( + device, + connectionOptions.getAutoConnect(), + connectionOptions.getRequestMTU(), + connectionOptions.getRefreshGattMoment(), + connectionOptions.getTimeoutInMillis(), + connectionOptions.getConnectionPriority(), + onSuccessCallback, onConnectionStateChangedCallback, onErrorCallback); + } + + @Override + public void cancelDeviceConnection(String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to cancel device connection", null)); + return; + } + + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + + if (connectingDevices.removeSubscription(deviceIdentifier) && device != null) { + onSuccessCallback.onSuccess(rxBleDeviceToDeviceMapper.map(device, null)); + } else { + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + } else { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceIdentifier)); + } + } + } + + @Override + public void isDeviceConnected(String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to check if device is connected", null)); + return; + } + + try { + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + return; + } + + boolean connected = device.getConnectionState() + .equals(RxBleConnection.RxBleConnectionState.CONNECTED); + onSuccessCallback.onSuccess(connected); + } catch (Exception e) { + onErrorCallback.onError(errorConverter.toError(e)); + } + } + + @Override + public void discoverAllServicesAndCharacteristicsForDevice(String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } + + safeDiscoverAllServicesAndCharacteristicsForDevice(device, transactionId, onSuccessCallback, onErrorCallback); + } + + @Override + public List getServicesForDevice(String deviceIdentifier) throws BleError { + final Device device = getDeviceById(deviceIdentifier); + final List services = device.getServices(); + if (services == null) { + throw BleErrorUtils.deviceServicesNotDiscovered(device.getId()); + } + return services; + } + + @Override + public List getCharacteristicsForDevice(String deviceIdentifier, + String serviceUUID) throws BleError { + final UUID convertedServiceUUID = UUIDConverter.convert(serviceUUID); + if (convertedServiceUUID == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID); + } + + final Device device = getDeviceById(deviceIdentifier); + + final Service service = device.getServiceByUUID(convertedServiceUUID); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); + } + + return service.getCharacteristics(); + } + + @Override + public List getCharacteristicsForService(int serviceIdentifier) throws BleError { + Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } + return service.getCharacteristics(); + } + + @Override + public List descriptorsForDevice(final String deviceIdentifier, + final String serviceUUID, + final String characteristicUUID) throws BleError { + final UUID[] uuids = UUIDConverter.convert(serviceUUID, characteristicUUID); + if (uuids == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID); + } + + Device device = getDeviceById(deviceIdentifier); + + final Service service = device.getServiceByUUID(uuids[0]); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); + } + + final Characteristic characteristic = service.getCharacteristicByUUID(uuids[1]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + return characteristic.getDescriptors(); + } + + @Override + public List descriptorsForService(final int serviceIdentifier, + final String characteristicUUID) throws BleError { + final UUID uuid = UUIDConverter.convert(characteristicUUID); + if (uuid == null) { + throw BleErrorUtils.invalidIdentifiers(characteristicUUID); + } + + Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } + + final Characteristic characteristic = service.getCharacteristicByUUID(uuid); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + return characteristic.getDescriptors(); + } + + @Override + public List descriptorsForCharacteristic(final int characteristicIdentifier) throws BleError { + Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); + } + + return characteristic.getDescriptors(); + } + + @Override + public void readCharacteristicForDevice(String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } + + @Override + public void readCharacteristicForService(int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } + + @Override + public void readCharacteristic(int characteristicIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; + } + + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } + + @Override + public void writeCharacteristicForDevice(String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void writeCharacteristicForService(int serviceIdentifier, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void writeCharacteristic(int characteristicIdentifier, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback + ); + } + + @Override + public void monitorCharacteristicForDevice(String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } + + @Override + public void monitorCharacteristicForService(int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } + + @Override + public void monitorCharacteristic(int characteristicIdentifier, String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; + } + + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } + + @Override + public void readDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + + try { + Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptorForService(final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + + try { + Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptor(final int descriptorIdentifier, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + try { + Descriptor descriptor = getDescriptor(descriptorIdentifier); + safeReadDescriptorForDevice(descriptor, transactionId, onSuccessCallback, onErrorCallback); + } catch (BleError error) { + onErrorCallback.onError(error); + } + } + + private void safeReadDescriptorForDevice(final Descriptor descriptor, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .readDescriptor(descriptor.getNativeDescriptor()) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + descriptor.logValue("Read from", bytes); + descriptor.setValue(bytes); + safeExecutor.success(new Descriptor(descriptor)); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Override + public void writeDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptorForService(final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptor(final int descriptorIdentifier, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(descriptorIdentifier); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + + } + + private void safeWriteDescriptorForDevice(final Descriptor descriptor, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + BluetoothGattDescriptor nativeDescriptor = descriptor.getNativeDescriptor(); + + if (nativeDescriptor.getUuid().equals(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID)) { + errorCallback.onError(BleErrorUtils.descriptorWriteNotAllowed(UUIDConverter.fromUUID(nativeDescriptor.getUuid()))); + return; + } + + final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), errorCallback); + if (connection == null) { + return; + } + + final byte[] value; + try { + value = Base64Converter.decode(valueBase64); + } catch (Throwable e) { + String uuid = UUIDConverter.fromUUID(nativeDescriptor.getUuid()); + errorCallback.onError(BleErrorUtils.invalidWriteDataForDescriptor(valueBase64, uuid)); + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(successCallback, errorCallback); + + final Disposable subscription = connection + .writeDescriptor(nativeDescriptor, value) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(() -> { + descriptor.logValue("Write to", value); + descriptor.setValue(value); + safeExecutor.success(new Descriptor(descriptor)); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + // Mark: Descriptors getters ------------------------------------------------------------------- + + private Descriptor getDescriptor(@NonNull final String deviceId, + @NonNull final String serviceUUID, + @NonNull final String characteristicUUID, + @NonNull final String descriptorUUID) throws BleError { + final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID, descriptorUUID); + if (UUIDs == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID, descriptorUUID); + } + + final Device device = connectedDevices.get(deviceId); + if (device == null) { + throw BleErrorUtils.deviceNotConnected(deviceId); + } + + final Service service = device.getServiceByUUID(UUIDs[0]); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); + } + + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[2]); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); + } + + return descriptor; + } + + private Descriptor getDescriptor(final int serviceIdentifier, + @NonNull final String characteristicUUID, + @NonNull final String descriptorUUID) throws BleError { + final UUID[] UUIDs = UUIDConverter.convert(characteristicUUID, descriptorUUID); + if (UUIDs == null) { + throw BleErrorUtils.invalidIdentifiers(characteristicUUID, descriptorUUID); + } + + final Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } + + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[0]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[1]); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); + } + + return descriptor; + } + + private Descriptor getDescriptor(final int characteristicIdentifier, + @NonNull final String descriptorUUID) throws BleError { + final UUID uuid = UUIDConverter.convert(descriptorUUID); + if (uuid == null) { + throw BleErrorUtils.invalidIdentifiers(descriptorUUID); + } + + final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); + } + + final Descriptor descriptor = characteristic.getDescriptorByUUID(uuid); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); + } + + return descriptor; + } + + private Descriptor getDescriptor(final int descriptorIdentifier) throws BleError { + + final Descriptor descriptor = discoveredDescriptors.get(descriptorIdentifier); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(Integer.toString(descriptorIdentifier)); + } + + return descriptor; + } + + @Override + public void cancelTransaction(String transactionId) { + pendingTransactions.removeSubscription(transactionId); + } + + public void setLogLevel(String logLevel) { + currentLogLevel = LogLevel.toLogLevel(logLevel); + RxBleLog.setLogLevel(currentLogLevel); + } + + @Override + public String getLogLevel() { + return LogLevel.fromLogLevel(currentLogLevel); + } + + private Disposable monitorAdapterStateChanges(Context context, + final OnEventCallback onAdapterStateChangeCallback) { + if (!supportsBluetoothLowEnergy()) { + return null; + } + + return new RxBleAdapterStateObservable(context) + .map(this::mapRxBleAdapterStateToLocalBluetoothState) + .subscribe(onAdapterStateChangeCallback::onEvent); + } + + private boolean supportsBluetoothLowEnergy() { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); + } + + @BluetoothState + private String mapRxBleAdapterStateToLocalBluetoothState( + RxBleAdapterStateObservable.BleAdapterState rxBleAdapterState + ) { + if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { + return BluetoothState.POWERED_ON; + } else if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_OFF) { + return BluetoothState.POWERED_OFF; + } else { + return BluetoothState.RESETTING; + } + } + + @SuppressLint("MissingPermission") + private void changeAdapterState(final RxBleAdapterStateObservable.BleAdapterState desiredAdapterState, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + if (bluetoothManager == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothStateChangeFailed, "BluetoothManager is null", null)); + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = new RxBleAdapterStateObservable(context) + .takeUntil(actualAdapterState -> desiredAdapterState == actualAdapterState) + .firstOrError() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(state -> { + safeExecutor.success(null); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + + boolean desiredAndInitialStateAreSame = false; + try { + if (desiredAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (context instanceof Activity) { + ((Activity) context).startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1); + desiredAndInitialStateAreSame = true; + } + } else { + desiredAndInitialStateAreSame = !bluetoothAdapter.enable(); + } + } else { + desiredAndInitialStateAreSame = !bluetoothAdapter.disable(); + } + } catch (SecurityException e) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothUnauthorized, + "Method requires BLUETOOTH_CONNECT permission", + null) + ); + } else { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothUnauthorized, + "Method requires BLUETOOTH_ADMIN permission", + null) + ); + } + } catch (Exception e) { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothStateChangeFailed, + String.format("Couldn't set bluetooth adapter state because of: %s", e.getMessage() != null ? e.getMessage() : "unknown error"), + null) + ); + } + if (desiredAndInitialStateAreSame) { + subscription.dispose(); + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothStateChangeFailed, + String.format("Couldn't set bluetooth adapter state to %s", desiredAdapterState.toString()), + null)); + } else { + pendingTransactions.replaceSubscription(transactionId, subscription); + } + } + + @BluetoothState + private String mapNativeAdapterStateToLocalBluetoothState(int adapterState) { + switch (adapterState) { + case BluetoothAdapter.STATE_OFF: + return BluetoothState.POWERED_OFF; + case BluetoothAdapter.STATE_ON: + return BluetoothState.POWERED_ON; + case BluetoothAdapter.STATE_TURNING_OFF: + // fallthrough + case BluetoothAdapter.STATE_TURNING_ON: + return BluetoothState.RESETTING; + default: + return BluetoothState.UNKNOWN; + } + } + + private void safeStartDeviceScan(final UUID[] uuids, + final int scanMode, + int callbackType, + final OnEventCallback onEventCallback, + final OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to start device scan", null)); + return; + } + + ScanSettings scanSettings = new ScanSettings.Builder() + .setScanMode(scanMode) + .setCallbackType(callbackType) + .build(); + + int length = uuids == null ? 0 : uuids.length; + ScanFilter[] filters = new ScanFilter[length]; + for (int i = 0; i < length; i++) { + filters[i] = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuids[i].toString())).build(); + } + + scanSubscription = rxBleClient + .scanBleDevices(scanSettings, filters) + .subscribe(scanResult -> { + String deviceId = scanResult.getBleDevice().getMacAddress(); + if (!discoveredDevices.containsKey(deviceId)) { + discoveredDevices.put(deviceId, rxBleDeviceToDeviceMapper.map(scanResult.getBleDevice(), null)); + } + onEventCallback.onEvent(rxScanResultToScanResultMapper.map(scanResult)); + }, throwable -> onErrorCallback.onError(errorConverter.toError(throwable))); + } + + @NonNull + private Device getDeviceById(@NonNull final String deviceId) throws BleError { + final Device device = connectedDevices.get(deviceId); + if (device == null) { + throw BleErrorUtils.deviceNotConnected(deviceId); + } + return device; + } + + @Nullable + private RxBleConnection getConnectionOrEmitError(@NonNull final String deviceId, + @NonNull OnErrorCallback onErrorCallback) { + final RxBleConnection connection = activeConnections.get(deviceId); + if (connection == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); + return null; + } + return connection; + } + + private void safeConnectToDevice(final RxBleDevice device, + final boolean autoConnect, + final int requestMtu, + final RefreshGattMoment refreshGattMoment, + final Long timeout, + final int connectionPriority, + final OnSuccessCallback onSuccessCallback, + final OnEventCallback onConnectionStateChangedCallback, + final OnErrorCallback onErrorCallback) { + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + Observable connect = device + .establishConnection(autoConnect) + .doOnSubscribe(disposable -> onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTING)) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + onDeviceDisconnected(device); + onConnectionStateChangedCallback.onEvent(ConnectionState.DISCONNECTED); + }); + + if (refreshGattMoment == RefreshGattMoment.ON_CONNECTED) { + connect = connect.flatMap(rxBleConnection -> rxBleConnection + .queue(new RefreshGattCustomOperation()) + .map(refreshGattSuccess -> rxBleConnection)); + } + + if (connectionPriority > 0) { + connect = connect.flatMap(rxBleConnection -> rxBleConnection + .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) + .andThen(Observable.just(rxBleConnection)) + ); + } + + if (requestMtu > 0) { + connect = connect.flatMap(rxBleConnection -> + rxBleConnection.requestMtu(requestMtu) + .map(integer -> rxBleConnection) + .toObservable() + ); + } + + if (timeout != null) { + connect = connect.timeout(timeout, TimeUnit.MILLISECONDS); + } + + + final Disposable subscription = connect + .subscribe(rxBleConnection -> { + Device localDevice = rxBleDeviceToDeviceMapper.map(device, rxBleConnection); + onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTED); + cleanServicesAndCharacteristicsForDevice(localDevice); + connectedDevices.put(device.getMacAddress(), localDevice); + activeConnections.put(device.getMacAddress(), rxBleConnection); + safeExecutor.success(localDevice); + }, error -> { + BleError bleError = errorConverter.toError(error); + safeExecutor.error(bleError); + onDeviceDisconnected(device); + }); + + connectingDevices.replaceSubscription(device.getMacAddress(), subscription); + } + + private void onDeviceDisconnected(RxBleDevice rxDevice) { + activeConnections.remove(rxDevice.getMacAddress()); + Device device = connectedDevices.remove(rxDevice.getMacAddress()); + if (device == null) { + return; + } + + cleanServicesAndCharacteristicsForDevice(device); + connectingDevices.removeSubscription(device.getId()); + } + + private void safeDiscoverAllServicesAndCharacteristicsForDevice(final Device device, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .discoverServices() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(rxBleDeviceServices -> { + ArrayList services = new ArrayList<>(); + for (BluetoothGattService gattService : rxBleDeviceServices.getBluetoothGattServices()) { + Service service = serviceFactory.create(device.getId(), gattService); + discoveredServices.put(service.getId(), service); + services.add(service); + + for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) { + Characteristic characteristic = new Characteristic(service, gattCharacteristic); + discoveredCharacteristics.put(characteristic.getId(), characteristic); + + for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { + Descriptor descriptor = new Descriptor(characteristic, gattDescriptor); + discoveredDescriptors.put(descriptor.getId(), descriptor); + } + } + } + device.setServices(services); + // Moved from onSuccess method from old RxJava1 implementation + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + private void safeReadCharacteristicForDevice(final Characteristic characteristic, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .readCharacteristic(characteristic.gattCharacteristic) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + characteristic.logValue("Read from", bytes); + characteristic.setValue(bytes); + safeExecutor.success(new Characteristic(characteristic)); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + private void writeCharacteristicWithValue(final Characteristic characteristic, + final String valueBase64, + final Boolean response, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final byte[] value; + try { + value = Base64Converter.decode(valueBase64); + } catch (Throwable error) { + onErrorCallback.onError( + BleErrorUtils.invalidWriteDataForCharacteristic(valueBase64, + UUIDConverter.fromUUID(characteristic.getUuid()))); + return; + } + + characteristic.setWriteType(response ? + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT : + BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); + + safeWriteCharacteristicForDevice( + characteristic, + value, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + private void safeWriteCharacteristicForDevice(final Characteristic characteristic, + final byte[] value, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .writeCharacteristic(characteristic.gattCharacteristic, value) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + characteristic.logValue("Write to", bytes); + characteristic.setValue(bytes); + safeExecutor.success(new Characteristic(characteristic)); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + private void safeMonitorCharacteristicForDevice(final Characteristic characteristic, + final String transactionId, + final OnEventCallback onEventCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(null, onErrorCallback); + + final Disposable subscription = Observable.defer(() -> { + BluetoothGattDescriptor cccDescriptor = characteristic.getGattDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); + NotificationSetupMode setupMode = cccDescriptor != null + ? NotificationSetupMode.QUICK_SETUP + : NotificationSetupMode.COMPAT; + if (characteristic.isNotifiable()) { + return connection.setupNotification(characteristic.gattCharacteristic, setupMode); + } + + if (characteristic.isIndicatable()) { + return connection.setupIndication(characteristic.gattCharacteristic, setupMode); + } + + return Observable.error(new CannotMonitorCharacteristicException(characteristic)); + }) + .flatMap(observable -> observable) + .toFlowable(BackpressureStrategy.BUFFER) + .observeOn(Schedulers.computation()) + .doOnCancel(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .doOnComplete(() -> pendingTransactions.removeSubscription(transactionId)) + .subscribe(bytes -> { + characteristic.logValue("Notification from", bytes); + characteristic.setValue(bytes); + onEventCallback.onEvent(new Characteristic(characteristic)); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Nullable + private Characteristic getCharacteristicOrEmitError(@NonNull final String deviceId, + @NonNull final String serviceUUID, + @NonNull final String characteristicUUID, + @NonNull final OnErrorCallback onErrorCallback) { + + final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID); + if (UUIDs == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID)); + return null; + } + + final Device device = connectedDevices.get(deviceId); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); + return null; + } + + final Service service = device.getServiceByUUID(UUIDs[0]); + if (service == null) { + onErrorCallback.onError(BleErrorUtils.serviceNotFound(serviceUUID)); + return null; + } + + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); + return null; + } + + return characteristic; + } + + @Nullable + private Characteristic getCharacteristicOrEmitError(final int serviceIdentifier, + @NonNull final String characteristicUUID, + @NonNull final OnErrorCallback onErrorCallback) { + + final UUID uuid = UUIDConverter.convert(characteristicUUID); + if (uuid == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(characteristicUUID)); + return null; + } + + final Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + onErrorCallback.onError(BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier))); + return null; + } + + final Characteristic characteristic = service.getCharacteristicByUUID(uuid); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); + return null; + } + + return characteristic; + } + + @Nullable + private Characteristic getCharacteristicOrEmitError(final int characteristicIdentifier, + @NonNull final OnErrorCallback onErrorCallback) { + + final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier))); + return null; + } + + return characteristic; + } + + private void cleanServicesAndCharacteristicsForDevice(@NonNull Device device) { + for (int i = discoveredServices.size() - 1; i >= 0; i--) { + int key = discoveredServices.keyAt(i); + Service service = discoveredServices.get(key); + + if (service.getDeviceID().equals(device.getId())) { + discoveredServices.remove(key); + } + } + for (int i = discoveredCharacteristics.size() - 1; i >= 0; i--) { + int key = discoveredCharacteristics.keyAt(i); + Characteristic characteristic = discoveredCharacteristics.get(key); + + if (characteristic.getDeviceId().equals(device.getId())) { + discoveredCharacteristics.remove(key); + } + } + + for (int i = discoveredDescriptors.size() - 1; i >= 0; i--) { + int key = discoveredDescriptors.keyAt(i); + Descriptor descriptor = discoveredDescriptors.get(key); + if (descriptor.getDeviceId().equals(device.getId())) { + discoveredDescriptors.remove(key); + } + } + } +} diff --git a/android/src/main/java/com/bleplx/adapter/Characteristic.java b/android/src/main/java/com/bleplx/adapter/Characteristic.java new file mode 100755 index 00000000..1e1fb7a8 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/Characteristic.java @@ -0,0 +1,151 @@ +package com.bleplx.adapter; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bleplx.adapter.utils.ByteUtils; +import com.bleplx.adapter.utils.Constants; +import com.bleplx.adapter.utils.IdGenerator; +import com.bleplx.adapter.utils.IdGeneratorKey; +import com.polidea.rxandroidble2.internal.RxBleLog; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** @noinspection ALL*/ +public class Characteristic { + + final private int id; + final private int serviceID; + final private UUID serviceUUID; + final private String deviceID; + private byte[] value; + final BluetoothGattCharacteristic gattCharacteristic; + + public void setValue(byte[] value) { + this.value = value; + } + + public Characteristic(@NonNull Service service, @NonNull BluetoothGattCharacteristic gattCharacteristic) { + this.deviceID = service.getDeviceID(); + this.serviceUUID = service.getUuid(); + this.serviceID = service.getId(); + this.gattCharacteristic = gattCharacteristic; + this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceID, gattCharacteristic.getUuid(), gattCharacteristic.getInstanceId())); + } + + public Characteristic(int id, @NonNull Service service, BluetoothGattCharacteristic gattCharacteristic) { + this.id = id; + this.deviceID = service.getDeviceID(); + this.serviceUUID = service.getUuid(); + this.serviceID = service.getId(); + this.gattCharacteristic = gattCharacteristic; + } + + public Characteristic(Characteristic other) { + id = other.id; + serviceID = other.serviceID; + serviceUUID = other.serviceUUID; + deviceID = other.deviceID; + if (other.value != null) value = other.value.clone(); + gattCharacteristic = other.gattCharacteristic; + } + + public int getId() { + return this.id; + } + + public UUID getUuid() { + return gattCharacteristic.getUuid(); + } + + public int getServiceID() { + return serviceID; + } + + public UUID getServiceUUID() { + return serviceUUID; + } + + public String getDeviceId() { + return deviceID; + } + + public int getInstanceId() { + return gattCharacteristic.getInstanceId(); + } + + public BluetoothGattDescriptor getGattDescriptor(UUID uuid) { + return gattCharacteristic.getDescriptor(uuid); + } + + public void setWriteType(int writeType) { + gattCharacteristic.setWriteType(writeType); + } + + public boolean isReadable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0; + } + + public boolean isWritableWithResponse() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0; + } + + public boolean isWritableWithoutResponse() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0; + } + + public boolean isNotifiable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0; + } + + public List getDescriptors() { + ArrayList descriptors = new ArrayList<>(gattCharacteristic.getDescriptors().size()); + for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { + descriptors.add(new Descriptor(this, gattDescriptor)); + } + return descriptors; + } + + public boolean isNotifying() { + BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); + boolean isNotifying = false; + if (descriptor != null) { + byte[] descriptorValue = descriptor.getValue(); + if (descriptorValue != null) { + isNotifying = (descriptorValue[0] & 0x01) != 0; + } + } + return isNotifying; + } + + public boolean isIndicatable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0; + } + + public byte[] getValue() { + return value; + } + + @Nullable + public Descriptor getDescriptorByUUID(@NonNull UUID uuid) { + BluetoothGattDescriptor descriptor = this.gattCharacteristic.getDescriptor(uuid); + if (descriptor == null) return null; + return new Descriptor(this, descriptor); + } + + void logValue(String message, byte[] value) { + if (value == null) { + value = gattCharacteristic.getValue(); + } + String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; + RxBleLog.v(message + + " Characteristic(uuid: " + gattCharacteristic.getUuid().toString() + + ", id: " + id + + ", value: " + hexValue + ")"); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java b/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java new file mode 100644 index 00000000..3041d204 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java @@ -0,0 +1,72 @@ +package com.bleplx.adapter; + +import androidx.annotation.Nullable; + +import com.bleplx.adapter.utils.Constants.ConnectionPriority; + +public class ConnectionOptions { + + /** + * Whether to directly connect to the remote device (false) or to automatically connect as soon + * as the remote device becomes available (true). + */ + private final boolean autoConnect; + + /** + * Whether MTU size will be negotiated to this value. It is not guaranteed to get it after + * connection is successful. + */ + private final int requestMTU; + + /** + * Whether action will be taken to reset services cache. This option may be useful when a + * peripheral's firmware was updated and it's services/characteristics were + * added/removed/altered. {@link ...} + */ + private final RefreshGattMoment refreshGattMoment; + + /** + * Number of milliseconds after connection is automatically timed out. In case of race condition + * were connection is established right after timeout event, device will be disconnected + * immediately. Time out may happen earlier then specified due to OS specific behavior. + */ + @Nullable + private final Long timeoutInMillis; + + @ConnectionPriority + private final int connectionPriority; + + public ConnectionOptions(Boolean autoConnect, + int requestMTU, + RefreshGattMoment refreshGattMoment, + @Nullable Long timeoutInMillis, + int connectionPriority) { + this.autoConnect = autoConnect; + this.requestMTU = requestMTU; + this.refreshGattMoment = refreshGattMoment; + this.timeoutInMillis = timeoutInMillis; + this.connectionPriority = connectionPriority; + } + + public Boolean getAutoConnect() { + return autoConnect; + } + + public int getRequestMTU() { + return requestMTU; + } + + public RefreshGattMoment getRefreshGattMoment() { + return refreshGattMoment; + } + + @Nullable + public Long getTimeoutInMillis() { + return timeoutInMillis; + } + + @ConnectionPriority + public int getConnectionPriority() { + return connectionPriority; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/ConnectionState.java b/android/src/main/java/com/bleplx/adapter/ConnectionState.java new file mode 100644 index 00000000..b76358e5 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/ConnectionState.java @@ -0,0 +1,12 @@ +package com.bleplx.adapter; + +public enum ConnectionState { + + CONNECTING("connecting"), CONNECTED("connected"), DISCONNECTING("disconnecting"), DISCONNECTED("disconnected"); + + public final String value; + + ConnectionState(String value) { + this.value = value; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/Descriptor.java b/android/src/main/java/com/bleplx/adapter/Descriptor.java new file mode 100644 index 00000000..103cc9aa --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/Descriptor.java @@ -0,0 +1,115 @@ +package com.bleplx.adapter; + +import android.bluetooth.BluetoothGattDescriptor; + +import androidx.annotation.NonNull; + +import com.bleplx.adapter.utils.ByteUtils; +import com.bleplx.adapter.utils.IdGenerator; +import com.bleplx.adapter.utils.IdGeneratorKey; +import com.polidea.rxandroidble2.internal.RxBleLog; + +import java.util.UUID; + +/** @noinspection unused*/ +public class Descriptor { + final private int characteristicId; + final private int serviceId; + final private UUID characteristicUuid; + final private UUID serviceUuid; + final private String deviceId; + final private BluetoothGattDescriptor descriptor; + final private int id; + final private UUID uuid; + private byte[] value = null; + + public Descriptor(@NonNull Characteristic characteristic, @NonNull BluetoothGattDescriptor gattDescriptor) { + this.characteristicId = characteristic.getId(); + this.characteristicUuid = characteristic.getUuid(); + this.serviceId = characteristic.getServiceID(); + this.serviceUuid = characteristic.getServiceUUID(); + this.descriptor = gattDescriptor; + this.deviceId = characteristic.getDeviceId(); + this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, descriptor.getUuid(), characteristicId)); + this.uuid = gattDescriptor.getUuid(); + } + + //secondary constructor, not used by MBA itself, but which can be used by external plugins (eg. BLEmulator) + public Descriptor(int characteristicId, int serviceId, UUID characteristicUuid, UUID serviceUuid, String deviceId, BluetoothGattDescriptor descriptor, int id, UUID uuid) { + this.characteristicId = characteristicId; + this.serviceId = serviceId; + this.characteristicUuid = characteristicUuid; + this.serviceUuid = serviceUuid; + this.deviceId = deviceId; + this.descriptor = descriptor; + this.id = id; + this.uuid = uuid; + } + + public Descriptor(Descriptor other) { + characteristicUuid = other.characteristicUuid; + characteristicId = other.characteristicId; + serviceUuid = other.serviceUuid; + serviceId = other.serviceId; + deviceId = other.deviceId; + descriptor = other.descriptor; + id = other.id; + uuid = other.uuid; + if (other.value != null) value = other.value.clone(); + } + + public int getId() { + return id; + } + + public String getDeviceId() { + return deviceId; + } + + public int getCharacteristicId() { + return characteristicId; + } + + public int getServiceId() { + return serviceId; + } + + public UUID getCharacteristicUuid() { + return characteristicUuid; + } + + public UUID getServiceUuid() { + return serviceUuid; + } + + public UUID getUuid() { + return uuid; + } + + public byte[] getValue() { + return value; + } + + public void setValue(byte[] value) { + this.value = value; + } + + public void setValueFromCache() { + value = descriptor.getValue(); + } + + public BluetoothGattDescriptor getNativeDescriptor() { + return descriptor; + } + + public void logValue(String message, byte[] value) { + if (value == null) { + value = descriptor.getValue(); + } + String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; + RxBleLog.v(message + + " Descriptor(uuid: " + descriptor.getUuid().toString() + + ", id: " + id + + ", value: " + hexValue + ")"); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/Device.java b/android/src/main/java/com/bleplx/adapter/Device.java new file mode 100755 index 00000000..05335267 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/Device.java @@ -0,0 +1,81 @@ +package com.bleplx.adapter; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; +import java.util.UUID; + +/** @noinspection unused*/ +public class Device { + + private String id; + private String name; + @Nullable + private Integer rssi; + @Nullable + private Integer mtu; + @Nullable + private List services; + + public Device(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Nullable + public Integer getRssi() { + return rssi; + } + + public void setRssi(@Nullable Integer rssi) { + this.rssi = rssi; + } + + @Nullable + public Integer getMtu() { + return mtu; + } + + public void setMtu(@Nullable Integer mtu) { + this.mtu = mtu; + } + + @Nullable + public List getServices() { + return services; + } + + public void setServices(@Nullable List services) { + this.services = services; + } + + @Nullable + public Service getServiceByUUID(@NonNull UUID uuid) { + if (services == null) { + return null; + } + + for (Service service : services) { + if (uuid.equals(service.getUuid())) + return service; + } + return null; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java b/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java new file mode 100644 index 00000000..8c6d1bba --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java @@ -0,0 +1,8 @@ +package com.bleplx.adapter; + +import com.bleplx.adapter.errors.BleError; + +public interface OnErrorCallback { + + void onError(BleError error); +} diff --git a/android/src/main/java/com/bleplx/adapter/OnEventCallback.java b/android/src/main/java/com/bleplx/adapter/OnEventCallback.java new file mode 100644 index 00000000..71d378e4 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/OnEventCallback.java @@ -0,0 +1,6 @@ +package com.bleplx.adapter; + +public interface OnEventCallback { + + void onEvent(T data); +} diff --git a/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java b/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java new file mode 100644 index 00000000..1dada60f --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java @@ -0,0 +1,6 @@ +package com.bleplx.adapter; + +public interface OnSuccessCallback { + + void onSuccess(T data); +} diff --git a/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java b/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java new file mode 100755 index 00000000..92435684 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java @@ -0,0 +1,20 @@ +package com.bleplx.adapter; + + +public enum RefreshGattMoment { + + ON_CONNECTED("OnConnected"); + + final String name; + + RefreshGattMoment(String name) { + this.name = name; + } + + public static RefreshGattMoment getByName(String name) { + for (RefreshGattMoment refreshGattMoment : RefreshGattMoment.values()) { + if (refreshGattMoment.name.equals(name)) return refreshGattMoment; + } + return null; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/ScanResult.java b/android/src/main/java/com/bleplx/adapter/ScanResult.java new file mode 100644 index 00000000..c3a89ba0 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/ScanResult.java @@ -0,0 +1,132 @@ +package com.bleplx.adapter; + + +import androidx.annotation.Nullable; + +import java.util.Arrays; +import java.util.Objects; +import java.util.UUID; + +/** + * @noinspection unused + */ +public class ScanResult { + + private String deviceId; + private String deviceName; + private int rssi; + private int mtu; + private boolean isConnectable; + @Nullable + private UUID[] overflowServiceUUIDs; + private AdvertisementData advertisementData; + + public ScanResult(String deviceId, String deviceName, int rssi, int mtu, boolean isConnectable, @Nullable UUID[] overflowServiceUUIDs, AdvertisementData advertisementData) { + this.deviceId = deviceId; + this.deviceName = deviceName; + this.rssi = rssi; + this.mtu = mtu; + this.isConnectable = isConnectable; + this.overflowServiceUUIDs = overflowServiceUUIDs; + this.advertisementData = advertisementData; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public int getRssi() { + return rssi; + } + + public void setRssi(int rssi) { + this.rssi = rssi; + } + + public int getMtu() { + return mtu; + } + + public void setMtu(int mtu) { + this.mtu = mtu; + } + + public boolean isConnectable() { + return isConnectable; + } + + public void setConnectable(boolean connectable) { + isConnectable = connectable; + } + + public UUID[] getOverflowServiceUUIDs() { + return overflowServiceUUIDs; + } + + public void setOverflowServiceUUIDs(@Nullable UUID[] overflowServiceUUIDs) { + this.overflowServiceUUIDs = overflowServiceUUIDs; + } + + public AdvertisementData getAdvertisementData() { + return advertisementData; + } + + public void setAdvertisementData(AdvertisementData advertisementData) { + this.advertisementData = advertisementData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ScanResult that = (ScanResult) o; + + if (rssi != that.rssi) return false; + if (mtu != that.mtu) return false; + if (isConnectable != that.isConnectable) return false; + if (!deviceId.equals(that.deviceId)) return false; + if (!Objects.equals(deviceName, that.deviceName)) + return false; + // Probably incorrect - comparing Object[] arrays with Arrays.equals + if (!Arrays.equals(overflowServiceUUIDs, that.overflowServiceUUIDs)) return false; + return Objects.equals(advertisementData, that.advertisementData); + } + + @Override + public int hashCode() { + int result = deviceId.hashCode(); + result = 31 * result + (deviceName != null ? deviceName.hashCode() : 0); + result = 31 * result + rssi; + result = 31 * result + mtu; + result = 31 * result + (isConnectable ? 1 : 0); + result = 31 * result + Arrays.hashCode(overflowServiceUUIDs); + result = 31 * result + (advertisementData != null ? advertisementData.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ScanResult{" + + "deviceId='" + deviceId + '\'' + + ", deviceName='" + deviceName + '\'' + + ", rssi=" + rssi + + ", mtu=" + mtu + + ", isConnectable=" + isConnectable + + ", overflowServiceUUIDs=" + Arrays.toString(overflowServiceUUIDs) + + ", advertisementData=" + advertisementData + + '}'; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/Service.java b/android/src/main/java/com/bleplx/adapter/Service.java new file mode 100755 index 00000000..68ff349d --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/Service.java @@ -0,0 +1,56 @@ +package com.bleplx.adapter; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** @noinspection unused*/ +public class Service { + + final private int id; + final private String deviceID; + final private BluetoothGattService btGattService; + + public Service(int id, String deviceID, BluetoothGattService btGattService) { + this.id = id; + this.deviceID = deviceID; + this.btGattService = btGattService; + } + + public int getId() { + return this.id; + } + + public UUID getUuid() { + return btGattService.getUuid(); + } + + public String getDeviceID() { + return deviceID; + } + + public boolean isPrimary() { + return btGattService.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY; + } + + @Nullable + public Characteristic getCharacteristicByUUID(@NonNull UUID uuid) { + BluetoothGattCharacteristic characteristic = btGattService.getCharacteristic(uuid); + if (characteristic == null) return null; + return new Characteristic(this, characteristic); + } + + public List getCharacteristics() { + ArrayList characteristics = new ArrayList<>(btGattService.getCharacteristics().size()); + for (BluetoothGattCharacteristic gattCharacteristic : btGattService.getCharacteristics()) { + characteristics.add(new Characteristic(this, gattCharacteristic)); + } + return characteristics; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleError.java b/android/src/main/java/com/bleplx/adapter/errors/BleError.java new file mode 100755 index 00000000..89a179a5 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/errors/BleError.java @@ -0,0 +1,32 @@ +package com.bleplx.adapter.errors; + + +public class BleError extends Throwable { + + public BleErrorCode errorCode; + public Integer androidCode; + public String reason; + public String deviceID; + public String serviceUUID; + public String characteristicUUID; + public String descriptorUUID; + public String internalMessage; + + public BleError(BleErrorCode errorCode, String reason, Integer androidCode) { + this.errorCode = errorCode; + this.reason = reason; + this.androidCode = androidCode; + } + + @Override + public String getMessage() { + return "Error code: " + errorCode + + ", android code: " + androidCode + + ", reason" + reason + + ", deviceId" + deviceID + + ", serviceUuid" + serviceUUID + + ", characteristicUuid" + characteristicUUID + + ", descriptorUuid" + descriptorUUID + + ", internalMessage" + internalMessage; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java b/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java new file mode 100755 index 00000000..bd17a8fc --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java @@ -0,0 +1,57 @@ +package com.bleplx.adapter.errors; + + +public enum BleErrorCode { + + UnknownError(0), + BluetoothManagerDestroyed(1), + OperationCancelled(2), + OperationTimedOut(3), + OperationStartFailed(4), + InvalidIdentifiers(5), + + BluetoothUnsupported(100), + BluetoothUnauthorized(101), + BluetoothPoweredOff(102), + BluetoothInUnknownState(103), + BluetoothResetting(104), + BluetoothStateChangeFailed(105), + + DeviceConnectionFailed(200), + DeviceDisconnected(201), + DeviceRSSIReadFailed(202), + DeviceAlreadyConnected(203), + DeviceNotFound(204), + DeviceNotConnected(205), + DeviceMTUChangeFailed(206), + + ServicesDiscoveryFailed(300), + IncludedServicesDiscoveryFailed(301), + ServiceNotFound(302), + ServicesNotDiscovered(303), + + CharacteristicsDiscoveryFailed(400), + CharacteristicWriteFailed(401), + CharacteristicReadFailed(402), + CharacteristicNotifyChangeFailed(403), + CharacteristicNotFound(404), + CharacteristicsNotDiscovered(405), + CharacteristicInvalidDataFormat(406), + + DescriptorsDiscoveryFailed(500), + DescriptorWriteFailed(501), + DescriptorReadFailed(502), + DescriptorNotFound(503), + DescriptorsNotDiscovered(504), + DescriptorInvalidDataFormat(505), + DescriptorWriteNotAllowed(506), + + ScanStartFailed(600), + LocationServicesDisabled(601); + + public final int code; + + BleErrorCode(int code) { + this.code = code; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java b/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java new file mode 100755 index 00000000..fed5c0a8 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java @@ -0,0 +1,86 @@ +package com.bleplx.adapter.errors; + + +import androidx.annotation.NonNull; + +public class BleErrorUtils { + + public static BleError cancelled() { + return new BleError(BleErrorCode.OperationCancelled, null, null); + } + + static public BleError invalidIdentifiers(@NonNull String... identifiers) { + StringBuilder identifiersJoined = new StringBuilder(); + for (String identifier: identifiers) { + identifiersJoined.append(identifier).append(", "); + } + + BleError bleError = new BleError(BleErrorCode.InvalidIdentifiers, null, null); + bleError.internalMessage = identifiersJoined.toString(); + return bleError; + } + + static public BleError deviceNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.DeviceNotFound, null, null); + bleError.deviceID = uuid; + return bleError; + } + + static public BleError deviceNotConnected(String uuid) { + BleError bleError = new BleError(BleErrorCode.DeviceNotConnected, null, null); + bleError.deviceID = uuid; + return bleError; + } + + static public BleError characteristicNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, null, null); + bleError.characteristicUUID = uuid; + return bleError; + } + + static public BleError invalidWriteDataForCharacteristic(String data, String uuid) { + BleError bleError = new BleError(BleErrorCode.CharacteristicInvalidDataFormat, null, null); + bleError.characteristicUUID = uuid; + bleError.internalMessage = data; + return bleError; + } + + static public BleError descriptorNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorNotFound, null, null); + bleError.descriptorUUID = uuid; + return bleError; + } + + static public BleError invalidWriteDataForDescriptor(String data, String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorInvalidDataFormat, null, null); + bleError.descriptorUUID = uuid; + bleError.internalMessage = data; + return bleError; + } + + static public BleError descriptorWriteNotAllowed(String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorWriteNotAllowed, null, null); + bleError.descriptorUUID = uuid; + return bleError; + } + + static public BleError serviceNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.ServiceNotFound, null, null); + bleError.serviceUUID = uuid; + return bleError; + } + + static public BleError cannotMonitorCharacteristic(String reason, String deviceID, String serviceUUID, String characteristicUUID) { + BleError bleError = new BleError(BleErrorCode.CharacteristicNotifyChangeFailed, reason, null); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } + + public static BleError deviceServicesNotDiscovered(String deviceID) { + BleError bleError = new BleError(BleErrorCode.ServicesNotDiscovered, null, null); + bleError.deviceID = deviceID; + return bleError; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java b/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java new file mode 100755 index 00000000..df3c4b6f --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java @@ -0,0 +1,219 @@ +package com.bleplx.adapter.errors; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; + +import com.bleplx.adapter.Characteristic; +import com.bleplx.adapter.exceptions.CannotMonitorCharacteristicException; +import com.bleplx.adapter.utils.UUIDConverter; +import com.polidea.rxandroidble2.exceptions.BleAlreadyConnectedException; +import com.polidea.rxandroidble2.exceptions.BleCannotSetCharacteristicNotificationException; +import com.polidea.rxandroidble2.exceptions.BleCharacteristicNotFoundException; +import com.polidea.rxandroidble2.exceptions.BleConflictingNotificationAlreadySetException; +import com.polidea.rxandroidble2.exceptions.BleDisconnectedException; +import com.polidea.rxandroidble2.exceptions.BleGattCallbackTimeoutException; +import com.polidea.rxandroidble2.exceptions.BleGattCannotStartException; +import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException; +import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException; +import com.polidea.rxandroidble2.exceptions.BleGattException; +import com.polidea.rxandroidble2.exceptions.BleGattOperationType; +import com.polidea.rxandroidble2.exceptions.BleScanException; +import com.polidea.rxandroidble2.exceptions.BleServiceNotFoundException; + +import java.util.UUID; +import java.util.concurrent.TimeoutException; + +public class ErrorConverter { + + public BleError toError(Throwable throwable) { + + // Custom exceptions ----------------------------------------------------------------------- + + if (throwable instanceof CannotMonitorCharacteristicException) { + CannotMonitorCharacteristicException exception = (CannotMonitorCharacteristicException) throwable; + Characteristic characteristic = exception.getCharacteristic(); + // TODO: Missing deviceID + return BleErrorUtils.cannotMonitorCharacteristic( + throwable.getMessage(), + null, + UUIDConverter.fromUUID(characteristic.getServiceUUID()), + UUIDConverter.fromUUID(characteristic.getUuid())); + } + + // RxSwift exceptions ---------------------------------------------------------------------- + + if (throwable instanceof TimeoutException) { + return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), null); + } + + // RxAndroidBle exceptions ----------------------------------------------------------------- + + if (throwable instanceof BleAlreadyConnectedException) { + // TODO: Missing deviceID + return new BleError(BleErrorCode.DeviceAlreadyConnected, throwable.getMessage(), null); + } + + if (throwable instanceof BleCannotSetCharacteristicNotificationException) { + BluetoothGattCharacteristic gattCharacteristic = ((BleCannotSetCharacteristicNotificationException) throwable).getBluetoothGattCharacteristic(); + BluetoothGattService gattService = gattCharacteristic.getService(); + // TODO: Missing deviceID + return BleErrorUtils.cannotMonitorCharacteristic( + throwable.getMessage(), + null, + UUIDConverter.fromUUID(gattService.getUuid()), + UUIDConverter.fromUUID(gattCharacteristic.getUuid())); + } + + + if (throwable instanceof BleCharacteristicNotFoundException) { + UUID uuid = ((BleCharacteristicNotFoundException) throwable).getCharacteristicUUID(); + BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, throwable.getMessage(), null); + bleError.characteristicUUID = UUIDConverter.fromUUID(uuid); + return bleError; + } + + if (throwable instanceof BleConflictingNotificationAlreadySetException) { + UUID characteristicUUID = ((BleConflictingNotificationAlreadySetException) throwable).getCharacteristicUuid(); + // TODO: Missing Service UUID and device ID + return BleErrorUtils.cannotMonitorCharacteristic(throwable.getMessage(), null, null, UUIDConverter.fromUUID(characteristicUUID)); + } + + if (throwable instanceof BleDisconnectedException) { + BleDisconnectedException bleDisconnectedException = (BleDisconnectedException) throwable; + BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, throwable.getMessage(), bleDisconnectedException.state); + bleError.deviceID = bleDisconnectedException.bluetoothDeviceAddress; + return bleError; + } + + if (throwable instanceof BleScanException) { + return toError((BleScanException) throwable); + } + + if (throwable instanceof BleServiceNotFoundException) { + BleError bleError = new BleError(BleErrorCode.ServiceNotFound, throwable.getMessage(), null); + bleError.serviceUUID = UUIDConverter.fromUUID(((BleServiceNotFoundException) throwable).getServiceUUID()); + return bleError; + } + + // RxAndroidBle (GATT) exceptions ---------------------------------------------------------- + + if (throwable instanceof BleGattCallbackTimeoutException) { + return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), ((BleGattCallbackTimeoutException) throwable).getStatus()); + } + + if (throwable instanceof BleGattCannotStartException) { + return new BleError(BleErrorCode.OperationStartFailed, throwable.getMessage(), ((BleGattCannotStartException) throwable).getStatus()); + } + + if (throwable instanceof BleGattCharacteristicException) { + BleGattCharacteristicException exception = (BleGattCharacteristicException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + UUIDConverter.fromUUID(exception.characteristic.getService().getUuid()), + UUIDConverter.fromUUID(exception.characteristic.getUuid()), + null); + } + + if (throwable instanceof BleGattDescriptorException) { + BleGattDescriptorException exception = (BleGattDescriptorException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getService().getUuid()), + UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getUuid()), + UUIDConverter.fromUUID(exception.descriptor.getUuid())); + } + + if (throwable instanceof BleGattException) { + BleGattException exception = (BleGattException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + null, + null, + null); + } + + return new BleError(BleErrorCode.UnknownError, throwable.toString(), null); + } + + private BleError toError(int code, String message, BleGattOperationType operationType, String deviceID, String serviceUUID, String characteristicUUID, String descriptorUUID) { + if (BleGattOperationType.CONNECTION_STATE == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.SERVICE_DISCOVERY == operationType) { + BleError bleError = new BleError(BleErrorCode.ServicesDiscoveryFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.CHARACTERISTIC_READ == operationType || BleGattOperationType.CHARACTERISTIC_CHANGED == operationType) { + BleError bleError = new BleError(BleErrorCode.CharacteristicReadFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } else if (BleGattOperationType.CHARACTERISTIC_WRITE == operationType || BleGattOperationType.CHARACTERISTIC_LONG_WRITE == operationType || BleGattOperationType.RELIABLE_WRITE_COMPLETED == operationType) { + BleError bleError = new BleError(BleErrorCode.CharacteristicWriteFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } else if (BleGattOperationType.DESCRIPTOR_READ == operationType) { + BleError bleError = new BleError(BleErrorCode.DescriptorReadFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + bleError.descriptorUUID = descriptorUUID; + return bleError; + } else if (BleGattOperationType.DESCRIPTOR_WRITE == operationType) { + BleError bleError = new BleError(BleErrorCode.DescriptorWriteFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + bleError.descriptorUUID = descriptorUUID; + return bleError; + } else if (BleGattOperationType.READ_RSSI == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceRSSIReadFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.ON_MTU_CHANGED == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceMTUChangeFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.CONNECTION_PRIORITY_CHANGE == operationType) { + // TODO: Handle? + } + + return new BleError(BleErrorCode.UnknownError, message, code); + } + + private BleError toError(BleScanException bleScanException) { + final int reason = bleScanException.getReason(); + switch (reason) { + case BleScanException.BLUETOOTH_DISABLED: + return new BleError(BleErrorCode.BluetoothPoweredOff, bleScanException.getMessage(), null); + case BleScanException.BLUETOOTH_NOT_AVAILABLE: + return new BleError(BleErrorCode.BluetoothUnsupported, bleScanException.getMessage(), null); + case BleScanException.LOCATION_PERMISSION_MISSING: + return new BleError(BleErrorCode.BluetoothUnauthorized, bleScanException.getMessage(), null); + case BleScanException.LOCATION_SERVICES_DISABLED: + return new BleError(BleErrorCode.LocationServicesDisabled, bleScanException.getMessage(), null); + case BleScanException.BLUETOOTH_CANNOT_START: + default: + return new BleError(BleErrorCode.ScanStartFailed, bleScanException.getMessage(), null); + } + } +} diff --git a/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java b/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java new file mode 100755 index 00000000..a76d06cb --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java @@ -0,0 +1,15 @@ +package com.bleplx.adapter.exceptions; + +import com.bleplx.adapter.Characteristic; + +public class CannotMonitorCharacteristicException extends RuntimeException { + private Characteristic characteristic; + + public CannotMonitorCharacteristicException(Characteristic characteristic) { + this.characteristic = characteristic; + } + + public Characteristic getCharacteristic() { + return characteristic; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java b/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java new file mode 100755 index 00000000..0acb5343 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java @@ -0,0 +1,12 @@ +package com.bleplx.adapter.utils; + +import android.util.Base64; + +public class Base64Converter { + public static String encode(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.NO_WRAP); + } + public static byte[] decode(String base64) { + return Base64.decode(base64, Base64.NO_WRAP); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java b/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java new file mode 100644 index 00000000..86bcbea4 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java @@ -0,0 +1,15 @@ +package com.bleplx.adapter.utils; + +public class ByteUtils { + private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for ( int j = 0; j < bytes.length; j++ ) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/Constants.java b/android/src/main/java/com/bleplx/adapter/utils/Constants.java new file mode 100755 index 00000000..20fec4fe --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/Constants.java @@ -0,0 +1,66 @@ +package com.bleplx.adapter.utils; + + +import androidx.annotation.IntDef; +import androidx.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.UUID; + +public interface Constants { + + @StringDef({ + BluetoothState.UNKNOWN, + BluetoothState.RESETTING, + BluetoothState.UNSUPPORTED, + BluetoothState.UNAUTHORIZED, + BluetoothState.POWERED_OFF, + BluetoothState.POWERED_ON} + ) + @Retention(RetentionPolicy.SOURCE) + @interface BluetoothState { + + String UNKNOWN = "Unknown"; + String RESETTING = "Resetting"; + String UNSUPPORTED = "Unsupported"; + String UNAUTHORIZED = "Unauthorized"; + String POWERED_OFF = "PoweredOff"; + String POWERED_ON = "PoweredOn"; + } + + @StringDef({ + BluetoothLogLevel.NONE, + BluetoothLogLevel.VERBOSE, + BluetoothLogLevel.DEBUG, + BluetoothLogLevel.INFO, + BluetoothLogLevel.WARNING, + BluetoothLogLevel.ERROR} + ) + @Retention(RetentionPolicy.SOURCE) + @interface BluetoothLogLevel { + + String NONE = "None"; + String VERBOSE = "Verbose"; + String DEBUG = "Debug"; + String INFO = "Info"; + String WARNING = "Warning"; + String ERROR = "Error"; + } + + @IntDef({ + ConnectionPriority.BALANCED, + ConnectionPriority.HIGH, + ConnectionPriority.LOW_POWER} + ) + @Retention(RetentionPolicy.SOURCE) + @interface ConnectionPriority { + + int BALANCED = 0; + int HIGH = 1; + int LOW_POWER = 2; + } + + int MINIMUM_MTU = 23; + UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java b/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java new file mode 100755 index 00000000..94f20ac5 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java @@ -0,0 +1,39 @@ +package com.bleplx.adapter.utils; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import io.reactivex.disposables.Disposable; + +public class DisposableMap { + + final private Map subscriptions = new HashMap<>(); + + public synchronized void replaceSubscription(String key, Disposable subscription) { + Disposable oldSubscription = subscriptions.put(key, subscription); + if (oldSubscription != null && !oldSubscription.isDisposed()) { + oldSubscription.dispose(); + } + } + + public synchronized boolean removeSubscription(String key) { + Disposable subscription = subscriptions.remove(key); + if (subscription == null) return false; + if (!subscription.isDisposed()) { + subscription.dispose(); + } + return true; + } + + public synchronized void removeAllSubscriptions() { + Iterator> it = subscriptions.entrySet().iterator(); + while (it.hasNext()) { + Disposable subscription = it.next().getValue(); + it.remove(); + if (!subscription.isDisposed()) { + subscription.dispose(); + } + } + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java b/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java new file mode 100755 index 00000000..94305d3b --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java @@ -0,0 +1,22 @@ +package com.bleplx.adapter.utils; + +import java.util.HashMap; + +public class IdGenerator { + private static HashMap idMap = new HashMap<>(); + private static int nextKey = 0; + + public static int getIdForKey(IdGeneratorKey idGeneratorKey) { + Integer id = idMap.get(idGeneratorKey); + if (id != null) { + return id; + } + idMap.put(idGeneratorKey, ++nextKey); + return nextKey; + } + + public static void clear() { + idMap.clear(); + nextKey = 0; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java b/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java new file mode 100755 index 00000000..ca193cd8 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java @@ -0,0 +1,37 @@ +package com.bleplx.adapter.utils; + + +import java.util.UUID; + +public class IdGeneratorKey { + + private final String deviceAddress; + private final UUID uuid; + private final int id; + + public IdGeneratorKey(String deviceAddress, UUID uuid, int id) { + this.deviceAddress = deviceAddress; + this.uuid = uuid; + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IdGeneratorKey that = (IdGeneratorKey) o; + + if (id != that.id) return false; + if (!deviceAddress.equals(that.deviceAddress)) return false; + return uuid.equals(that.uuid); + } + + @Override + public int hashCode() { + int result = deviceAddress.hashCode(); + result = 31 * result + uuid.hashCode(); + result = 31 * result + id; + return result; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java b/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java new file mode 100755 index 00000000..11f8628e --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java @@ -0,0 +1,47 @@ +package com.bleplx.adapter.utils; + + +import com.polidea.rxandroidble2.internal.RxBleLog; + +public class LogLevel { + + @RxBleLog.LogLevel + public static int toLogLevel(String logLevel) { + switch (logLevel) { + case Constants.BluetoothLogLevel.VERBOSE: + return RxBleLog.VERBOSE; + case Constants.BluetoothLogLevel.DEBUG: + return RxBleLog.DEBUG; + case Constants.BluetoothLogLevel.INFO: + return RxBleLog.INFO; + case Constants.BluetoothLogLevel.WARNING: + return RxBleLog.WARN; + case Constants.BluetoothLogLevel.ERROR: + return RxBleLog.ERROR; + case Constants.BluetoothLogLevel.NONE: + // fallthrough + default: + return RxBleLog.NONE; + } + } + + @Constants.BluetoothLogLevel + public static String fromLogLevel(int logLevel) { + switch (logLevel) { + case RxBleLog.VERBOSE: + return Constants.BluetoothLogLevel.VERBOSE; + case RxBleLog.DEBUG: + return Constants.BluetoothLogLevel.DEBUG; + case RxBleLog.INFO: + return Constants.BluetoothLogLevel.INFO; + case RxBleLog.WARN: + return Constants.BluetoothLogLevel.WARNING; + case RxBleLog.ERROR: + return Constants.BluetoothLogLevel.ERROR; + case RxBleLog.NONE: + // fallthrough + default: + return Constants.BluetoothLogLevel.NONE; + } + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java b/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java new file mode 100755 index 00000000..20b12f18 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java @@ -0,0 +1,48 @@ +package com.bleplx.adapter.utils; + +import android.bluetooth.BluetoothGatt; + +import androidx.annotation.NonNull; + +import com.polidea.rxandroidble2.RxBleCustomOperation; +import com.polidea.rxandroidble2.internal.RxBleLog; +import com.polidea.rxandroidble2.internal.connection.RxBleGattCallback; + +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; +import io.reactivex.Scheduler; + +public class RefreshGattCustomOperation implements RxBleCustomOperation { + + /** @noinspection unchecked, JavaReflectionMemberAccess, DataFlowIssue */ + @NonNull + @Override + public Observable asObservable( + final BluetoothGatt bluetoothGatt, + final RxBleGattCallback rxBleGattCallback, + final Scheduler scheduler + ) { + return Observable.ambArray( + Observable.fromCallable(() -> { + boolean success = false; + try { + Method bluetoothGattRefreshFunction = bluetoothGatt.getClass().getMethod("refresh"); + + success = (Boolean) bluetoothGattRefreshFunction.invoke(bluetoothGatt); + + if (!success) RxBleLog.d("BluetoothGatt.refresh() returned false"); + } catch (Exception e) { + RxBleLog.d(e, "Could not call function BluetoothGatt.refresh()"); + } + + RxBleLog.i("Calling BluetoothGatt.refresh() status: %s", success ? "Success" : "Failure"); + return success; + }) + .subscribeOn(scheduler) + .delay(1, TimeUnit.SECONDS, scheduler), + rxBleGattCallback.observeDisconnect() + ); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java b/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java new file mode 100644 index 00000000..c4943fb9 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java @@ -0,0 +1,33 @@ +package com.bleplx.adapter.utils; + +import androidx.annotation.Nullable; + +import com.bleplx.adapter.OnErrorCallback; +import com.bleplx.adapter.OnSuccessCallback; +import com.bleplx.adapter.errors.BleError; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class SafeExecutor { + + private final OnSuccessCallback successCallback; + private final OnErrorCallback errorCallback; + private final AtomicBoolean wasExecuted = new AtomicBoolean(false); + + public SafeExecutor(@Nullable OnSuccessCallback successCallback, @Nullable OnErrorCallback errorCallback) { + this.successCallback = successCallback; + this.errorCallback = errorCallback; + } + + public void success(T data) { + if (wasExecuted.compareAndSet(false, true) && successCallback != null) { + successCallback.onSuccess(data); + } + } + + public void error(BleError error) { + if (wasExecuted.compareAndSet(false, true) && errorCallback != null) { + errorCallback.onError(error); + } + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java b/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java new file mode 100644 index 00000000..cd58e485 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java @@ -0,0 +1,16 @@ +package com.bleplx.adapter.utils; + +import android.bluetooth.BluetoothGattService; + +import com.bleplx.adapter.Service; + +public class ServiceFactory { + + public Service create(String deviceId, BluetoothGattService btGattService) { + return new Service( + IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, btGattService.getUuid(), btGattService.getInstanceId())), + deviceId, + btGattService + ); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java b/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java new file mode 100755 index 00000000..141172d2 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java @@ -0,0 +1,51 @@ +package com.bleplx.adapter.utils; + + +import java.util.UUID; + +public class UUIDConverter { + + private static String baseUUIDPrefix = "0000"; + private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB"; + + public static UUID convert(String sUUID) { + if (sUUID == null) return null; + + if (sUUID.length() == 4) { + sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; + } else if (sUUID.length() == 8) { + sUUID = sUUID + baseUUIDSuffix; + } + + try { + return UUID.fromString(sUUID); + } catch (Throwable e) { + return null; + } + } + + public static UUID[] convert(String... sUUIDs) { + UUID[] UUIDs = new UUID[sUUIDs.length]; + for (int i = 0; i < sUUIDs.length; i++) { + + if (sUUIDs[i] == null) return null; + + if (sUUIDs[i].length() == 4) { + sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix; + } else if (sUUIDs[i].length() == 8) { + sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix; + } + + try { + UUIDs[i] = UUID.fromString(sUUIDs[i]); + } catch (Throwable e) { + return null; + } + } + return UUIDs; + } + + public static String fromUUID(UUID uuid) { + return uuid.toString().toLowerCase(); + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java new file mode 100644 index 00000000..67c42229 --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java @@ -0,0 +1,19 @@ +package com.bleplx.adapter.utils.mapper; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bleplx.adapter.Device; +import com.polidea.rxandroidble2.RxBleConnection; +import com.polidea.rxandroidble2.RxBleDevice; + +public class RxBleDeviceToDeviceMapper { + + public Device map(@NonNull RxBleDevice rxDevice, @Nullable RxBleConnection connection) { + Device device = new Device(rxDevice.getMacAddress(), rxDevice.getName()); + if (connection != null) { + device.setMtu(connection.getMtu()); + } + return device; + } +} diff --git a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java new file mode 100644 index 00000000..3417b63a --- /dev/null +++ b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java @@ -0,0 +1,20 @@ +package com.bleplx.adapter.utils.mapper; + +import com.bleplx.adapter.AdvertisementData; +import com.bleplx.adapter.ScanResult; +import com.bleplx.adapter.utils.Constants; + +public class RxScanResultToScanResultMapper { + + public ScanResult map(com.polidea.rxandroidble2.scan.ScanResult rxScanResult) { + return new ScanResult( + rxScanResult.getBleDevice().getMacAddress(), + rxScanResult.getBleDevice().getName(), + rxScanResult.getRssi(), + Constants.MINIMUM_MTU, + false, //isConnectable flag is not available on Android + null, //overflowServiceUUIDs are not available on Android + AdvertisementData.parseScanResponseData(rxScanResult.getScanRecord().getBytes()) + ); + } +} diff --git a/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java index ac15a1b7..22168eb3 100644 --- a/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java @@ -4,7 +4,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.errors.BleError; +import com.bleplx.adapter.errors.BleError; public class BleErrorToJsObjectConverter { @@ -63,4 +63,4 @@ private void appendString(StringBuilder stringBuilder, String key, String value) stringBuilder.append("\""); } } -} \ No newline at end of file +} diff --git a/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java index 1be25281..292499bf 100644 --- a/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java @@ -2,9 +2,9 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.Characteristic; -import com.polidea.multiplatformbleadapter.utils.Base64Converter; -import com.polidea.multiplatformbleadapter.utils.UUIDConverter; +import com.bleplx.adapter.Characteristic; +import com.bleplx.adapter.utils.Base64Converter; +import com.bleplx.adapter.utils.UUIDConverter; public class CharacteristicToJsObjectConverter extends JSObjectConverter { diff --git a/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java index 68e9f52f..88cb16c0 100644 --- a/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java @@ -2,9 +2,9 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.Descriptor; -import com.polidea.multiplatformbleadapter.utils.Base64Converter; -import com.polidea.multiplatformbleadapter.utils.UUIDConverter; +import com.bleplx.adapter.Descriptor; +import com.bleplx.adapter.utils.Base64Converter; +import com.bleplx.adapter.utils.UUIDConverter; public class DescriptorToJsObjectConverter extends JSObjectConverter { @@ -36,4 +36,4 @@ public WritableMap toJSObject(Descriptor descriptor) { js.putString(Metadata.VALUE, descriptor.getValue() != null ? Base64Converter.encode(descriptor.getValue()) : null); return js; } -} \ No newline at end of file +} diff --git a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java index e4e7166c..f439c761 100644 --- a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java @@ -2,8 +2,8 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.Device; -import com.polidea.multiplatformbleadapter.utils.Constants; +import com.bleplx.adapter.Device; +import com.bleplx.adapter.utils.Constants; public class DeviceToJsObjectConverter extends JSObjectConverter { diff --git a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java index eabe3dbd..40cb4ff6 100644 --- a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java @@ -5,10 +5,10 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.AdvertisementData; -import com.polidea.multiplatformbleadapter.ScanResult; -import com.polidea.multiplatformbleadapter.utils.Base64Converter; -import com.polidea.multiplatformbleadapter.utils.UUIDConverter; +import com.bleplx.adapter.AdvertisementData; +import com.bleplx.adapter.ScanResult; +import com.bleplx.adapter.utils.Base64Converter; +import com.bleplx.adapter.utils.UUIDConverter; import java.util.Map; import java.util.UUID; diff --git a/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java index f713ac7a..f1af91b3 100644 --- a/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java @@ -2,8 +2,8 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.polidea.multiplatformbleadapter.Service; -import com.polidea.multiplatformbleadapter.utils.UUIDConverter; +import com.bleplx.adapter.Service; +import com.bleplx.adapter.utils.UUIDConverter; public class ServiceToJsObjectConverter extends JSObjectConverter { From 1b5ff08e7391b359d0d986f85a49b1d4d4f27ced Mon Sep 17 00:00:00 2001 From: Kacper Cyranowski Date: Tue, 10 Oct 2023 08:00:47 +0200 Subject: [PATCH 3/8] chore: remove travis.yml --- .travis.yml | 126 ---------------------------------------------------- 1 file changed, 126 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c68541a2..00000000 --- a/.travis.yml +++ /dev/null @@ -1,126 +0,0 @@ -stages: - - name: self-test - if: type = push - - name: integration - if: branch = master OR type = pull_request - -env: - global: - - SYS=22 - - ABI="default;armeabi-v7a" - - ADB_INSTALL_TIMEOUT=10 # minutes - - EMULATOR_VERSION=4969155 # ver 27.3.10. - -_integration_job_template: &integration_job_template - stage: integration - cache: npm - install: - - nvm install 11 - - node --version - - gem install xcodeproj - - npm install - - npm install -g yarn - - npm install -g react-native-cli - before_script: - - integration-tests/scripts/make-project $RN_VERSION integration-tests/Setup - -_android_job_template: &android_job_template - <<: *integration_job_template - language: generic - dist: trusty - before_install: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start - - sleep 3 # give xvfb some time to start - # Install android tools - - ANDROID_TOOLS=4333796 # android-28 - - export ANDROID_HOME=~/android-sdk - - wget -q "https://dl.google.com/android/repository/sdk-tools-linux-$ANDROID_TOOLS.zip" -O android-sdk-tools.zip - - unzip -q android-sdk-tools.zip -d ${ANDROID_HOME} - - rm android-sdk-tools.zip - - PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools - # Silence warning. - - mkdir -p ~/.android - - touch ~/.android/repositories.cfg - # Accept licenses before installing components, no need to echo y for each component - - yes | sdkmanager --licenses - # Platform tools - - sdkmanager "tools" "platform-tools" > /dev/null - - sdkmanager --list | head -15 - # Android emulator - - wget -q "https://dl.google.com/android/repository/emulator-linux-$EMULATOR_VERSION.zip" -O emulator.zip - - unzip -q emulator.zip -d ${ANDROID_HOME} - - rm emulator.zip - # install older build tools (for emulator) - - sdkmanager "build-tools;28.0.3" "build-tools;25.0.2" "platforms;android-28" "platforms;android-25" > /dev/null - # Create and start emulator. - - sdkmanager "system-images;android-$SYS;$ABI" > /dev/null - - sdkmanager --list | head -15 - - echo no | avdmanager create avd --force -n test -k "system-images;android-$SYS;$ABI" - - $ANDROID_HOME/emulator/emulator -avd test -no-audio -no-window -no-boot-anim & - # before_script will make project - script: - - integration-tests/scripts/wait-for-emulator - - adb shell settings put global window_animation_scale 0 & - - adb shell settings put global transition_animation_scale 0 & - - adb shell settings put global animator_duration_scale 0 & - - adb shell input keyevent 82 & - - cd integration-tests/Setup - - node_modules/.bin/flow check - - cd android - - npm start & - - sleep 5 - - curl -s http://localhost:8081/index.bundle\?platform\=android\&dev\=true\&minify\=false > /dev/null - - ./gradlew connectedAndroidTest - -_ios_job_template: &ios_job_template - <<: *integration_job_template - language: objective-c - os: osx - osx_image: xcode11.4 - xcode_project: integration-tests/Setup/ios/Setup.xcodeproj - xcode_scheme: Setup - xcode_destination: platform=iOS Simulator - # before_script will make project - script: - - travis_wait 30 integration-tests/scripts/build-ios-project integration-tests/Setup - -matrix: - include: - - language: node_js - stage: self-test - node_js: 10 - script: - - npm install - - npm run lint - - npm test - - # Integration tests for different React Native versions - - - <<: *android_job_template - name: 'Integrate with RN 0.63.3 Android' - env: RN_VERSION=0.63.3 - - <<: *ios_job_template - name: 'Integrate with RN 0.63.3 iOS' - env: RN_VERSION=0.63.3 - - - <<: *android_job_template - name: 'Integrate with RN 0.62.2 Android' - env: RN_VERSION=0.62.2 - - <<: *ios_job_template - name: 'Integrate with RN 0.62.2 iOS' - env: RN_VERSION=0.62.2 - - - <<: *android_job_template - name: 'Integrate with RN 0.61.5 Android' - env: RN_VERSION=0.61.5 - - <<: *ios_job_template - name: 'Integrate with RN 0.61.5 iOS' - env: RN_VERSION=0.61.5 - - - <<: *android_job_template - name: 'Integrate with RN 0.60.6 Android' - env: RN_VERSION=0.60.6 - - <<: *ios_job_template - name: 'Integrate with RN 0.60.6 iOS' - env: RN_VERSION=0.60.6 From 410ca2033b1587ebe3e56b2e01445fcec6d11709 Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski Date: Fri, 13 Oct 2023 14:41:14 +0200 Subject: [PATCH 4/8] feat: added is connectable from scan result object --- .../main/java/com/bleplx/adapter/Device.java | 1 - .../RxScanResultToScanResultMapper.java | 24 ++++++++++--------- .../ScanResultToJsObjectConverter.java | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/android/src/main/java/com/bleplx/adapter/Device.java b/android/src/main/java/com/bleplx/adapter/Device.java index 05335267..29d8780a 100755 --- a/android/src/main/java/com/bleplx/adapter/Device.java +++ b/android/src/main/java/com/bleplx/adapter/Device.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.UUID; -/** @noinspection unused*/ public class Device { private String id; diff --git a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java index 3417b63a..2cf44175 100644 --- a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java +++ b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java @@ -1,20 +1,22 @@ package com.bleplx.adapter.utils.mapper; +import static com.polidea.rxandroidble2.scan.IsConnectable.CONNECTABLE; + import com.bleplx.adapter.AdvertisementData; import com.bleplx.adapter.ScanResult; import com.bleplx.adapter.utils.Constants; public class RxScanResultToScanResultMapper { - public ScanResult map(com.polidea.rxandroidble2.scan.ScanResult rxScanResult) { - return new ScanResult( - rxScanResult.getBleDevice().getMacAddress(), - rxScanResult.getBleDevice().getName(), - rxScanResult.getRssi(), - Constants.MINIMUM_MTU, - false, //isConnectable flag is not available on Android - null, //overflowServiceUUIDs are not available on Android - AdvertisementData.parseScanResponseData(rxScanResult.getScanRecord().getBytes()) - ); - } + public ScanResult map(com.polidea.rxandroidble2.scan.ScanResult rxScanResult) { + return new ScanResult( + rxScanResult.getBleDevice().getMacAddress(), + rxScanResult.getBleDevice().getName(), + rxScanResult.getRssi(), + Constants.MINIMUM_MTU, + rxScanResult.isConnectable() == CONNECTABLE, + null, //overflowServiceUUIDs are not available on Android + AdvertisementData.parseScanResponseData(rxScanResult.getScanRecord().getBytes()) + ); + } } diff --git a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java index 40cb4ff6..87ed35ea 100644 --- a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java @@ -38,6 +38,7 @@ public WritableMap toJSObject(@NonNull ScanResult scanResult) { result.putString(Metadata.NAME, scanResult.getDeviceName()); result.putInt(Metadata.RSSI, scanResult.getRssi()); result.putInt(Metadata.MTU, scanResult.getMtu()); + result.putBoolean(Metadata.IS_CONNECTABLE, scanResult.isConnectable()); AdvertisementData advData = scanResult.getAdvertisementData(); result.putString(Metadata.MANUFACTURER_DATA, @@ -88,7 +89,6 @@ public WritableMap toJSObject(@NonNull ScanResult scanResult) { } // Attributes which are not accessible on Android - result.putNull(Metadata.IS_CONNECTABLE); result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS); return result; } From c1cdb0368e27c5af8ec83f3d5867000dae711015 Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski <97128556+gmiszewski-intent@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:53:48 +0200 Subject: [PATCH 5/8] fix: resolves #1103 - crash on disconnect (#1111) * chore: recreate native error * test: android device disconnecting example * fix: resolves #1103 - crash on disconnect * refactor: sorted imports * refactor: types * fix: fast reuse of connect to device leads to 'device not connected error' * fix: fast reuse of connect to device leads to 'device not connected error' (only for Android) * refactor: change numbers into variables --- .eslintignore | 3 +- .../main/java/com/bleplx/BlePlxModule.java | 16 +- .../TestStateDisplay/TestStateDisplay.tsx | 8 +- example/src/consts/nRFDeviceConsts.ts | 13 + .../src/navigation/navigators/MainStack.tsx | 8 + .../DashboardScreen/DashboardScreen.tsx | 5 + .../DeviceConnectDisconnectTestScreen.tsx | 233 ++++++++++++++++++ .../DevicenRFTestScreen.tsx | 25 +- example/src/screens/MainStack/index.ts | 1 + example/src/services/BLEService/BLEService.ts | 31 ++- example/{ => src}/types/TestStateType.ts | 0 example/{ => src}/types/index.ts | 0 src/BleManager.js | 4 + 13 files changed, 321 insertions(+), 26 deletions(-) create mode 100644 example/src/consts/nRFDeviceConsts.ts create mode 100644 example/src/screens/MainStack/DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen.tsx rename example/{ => src}/types/TestStateType.ts (100%) rename example/{ => src}/types/index.ts (100%) diff --git a/.eslintignore b/.eslintignore index 3ef8f41d..fddf3ca8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ /**/node_modules/* node_modules/ -docs/** \ No newline at end of file +docs/** +lib/** \ No newline at end of file diff --git a/android/src/main/java/com/bleplx/BlePlxModule.java b/android/src/main/java/com/bleplx/BlePlxModule.java index 1464bd27..b165a1b3 100644 --- a/android/src/main/java/com/bleplx/BlePlxModule.java +++ b/android/src/main/java/com/bleplx/BlePlxModule.java @@ -36,22 +36,36 @@ import com.bleplx.converter.ServiceToJsObjectConverter; import com.bleplx.utils.ReadableArrayConverter; import com.bleplx.utils.SafePromise; +import com.polidea.rxandroidble2.internal.RxBleLog; import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import android.app.Activity; +import io.reactivex.exceptions.UndeliverableException; +import io.reactivex.plugins.RxJavaPlugins; + @ReactModule(name = BlePlxModule.NAME) public class BlePlxModule extends ReactContextBaseJavaModule { public static final String NAME = "BlePlx"; public BlePlxModule(ReactApplicationContext reactContext) { super(reactContext); + RxJavaPlugins.setErrorHandler(throwable -> { + if (throwable instanceof UndeliverableException) { + RxBleLog.e("Handle all unhandled exceptions from RxJava: " + throwable.getMessage()); + } else { + Thread currentThread = Thread.currentThread(); + Thread.UncaughtExceptionHandler errorHandler = currentThread.getUncaughtExceptionHandler(); + if (errorHandler != null) { + errorHandler.uncaughtException(currentThread, throwable); + } + } + }); } @Override diff --git a/example/src/components/atoms/TestStateDisplay/TestStateDisplay.tsx b/example/src/components/atoms/TestStateDisplay/TestStateDisplay.tsx index 44f88be0..29366514 100644 --- a/example/src/components/atoms/TestStateDisplay/TestStateDisplay.tsx +++ b/example/src/components/atoms/TestStateDisplay/TestStateDisplay.tsx @@ -1,11 +1,11 @@ import React from 'react' -import type { TestStateType } from 'example/types' +import type { TestStateType } from '../../../types' import { AppText } from '../AppText/AppText' import { Container, Header, Label } from './TestStateDisplay.styled' export type TestStateDisplayProps = { label?: string - state: TestStateType + state?: TestStateType value?: string } @@ -21,9 +21,9 @@ export function TestStateDisplay({ label, state, value }: TestStateDisplayProps)
- {marks[state]} + {!!state && {marks[state]}}
- {value && {value}} + {!!value && {value}}
) } diff --git a/example/src/consts/nRFDeviceConsts.ts b/example/src/consts/nRFDeviceConsts.ts new file mode 100644 index 00000000..66395ca6 --- /dev/null +++ b/example/src/consts/nRFDeviceConsts.ts @@ -0,0 +1,13 @@ +import { fullUUID } from 'react-native-ble-plx' +import base64 from 'react-native-base64' +import { getDateAsBase64 } from '../utils/getDateAsBase64' + +export const deviceTimeService = fullUUID('1847') +export const currentTimeCharacteristic = fullUUID('2A2B') +export const deviceTimeCharacteristic = fullUUID('2B90') +export const currentTimeCharacteristicTimeTriggerDescriptor = fullUUID('290E') + +export const writeWithResponseBase64Time = getDateAsBase64(new Date('2022-08-11T08:17:19Z')) +export const writeWithoutResponseBase64Time = getDateAsBase64(new Date('2023-09-12T10:12:16Z')) +export const monitorExpectedMessage = 'Hi, it works!' +export const currentTimeCharacteristicTimeTriggerDescriptorValue = base64.encode('BLE-PLX') diff --git a/example/src/navigation/navigators/MainStack.tsx b/example/src/navigation/navigators/MainStack.tsx index adbabf63..8efa01a2 100644 --- a/example/src/navigation/navigators/MainStack.tsx +++ b/example/src/navigation/navigators/MainStack.tsx @@ -7,6 +7,7 @@ export type MainStackParamList = { DASHBOARD_SCREEN: undefined DEVICE_DETAILS_SCREEN: undefined DEVICE_NRF_TEST_SCREEN: undefined + DEVICE_CONNECT_DISCONNECT_TEST_SCREEN: undefined } const MainStack = createNativeStackNavigator() @@ -37,6 +38,13 @@ export function MainStackComponent() { headerTitle: 'nRF device test' }} /> + ) } diff --git a/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx b/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx index d2c5e85b..0fc6c65f 100644 --- a/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx +++ b/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx @@ -79,6 +79,11 @@ export function DashboardScreen({ navigation }: DashboardScreenProps) { /> navigation.navigate('DEVICE_NRF_TEST_SCREEN')} /> + BLEService.isDeviceWithIdConnected('asd')} /> + navigation.navigate('DEVICE_CONNECT_DISCONNECT_TEST_SCREEN')} + /> +const NUMBER_OF_CALLS_IN_THE_TEST_SCENARIO = 10 + +export function DeviceConnectDisconnectTestScreen(_props: DeviceConnectDisconnectTestScreenProps) { + const [expectedDeviceName, setExpectedDeviceName] = useState('') + const [testScanDevicesState, setTestScanDevicesState] = useState('WAITING') + const [deviceId, setDeviceId] = useState('') + const [connectCounter, setConnectCounter] = useState(0) + const [characteristicDiscoverCounter, setCharacteristicDiscoverCounter] = useState(0) + const [connectInDisconnectTestCounter, setConnectInDisconnectTestCounter] = useState(0) + const [disconnectCounter, setDisconnectCounter] = useState(0) + const [monitorMessages, setMonitorMessages] = useState([]) + const monitorSubscriptionRef = useRef(null) + + const addMonitorMessage = (message: string) => setMonitorMessages(prevMessages => [...prevMessages, message]) + + const checkDeviceName = (device: Device) => + device.name?.toLocaleLowerCase() === expectedDeviceName.toLocaleLowerCase() + + const startConnectAndDiscover = async () => { + setTestScanDevicesState('IN_PROGRESS') + await BLEService.initializeBLE() + await BLEService.scanDevices(connectAndDiscoverOnDeviceFound, [deviceTimeService]) + } + + const startConnectAndDisconnect = async () => { + setTestScanDevicesState('IN_PROGRESS') + await BLEService.initializeBLE() + await BLEService.scanDevices(connectAndDisconnectOnDeviceFound, [deviceTimeService]) + } + + const startConnectOnly = async () => { + setTestScanDevicesState('IN_PROGRESS') + await BLEService.initializeBLE() + await BLEService.scanDevices( + async (device: Device) => { + if (checkDeviceName(device)) { + console.info(`connecting to ${device.id}`) + await startConnectToDevice(device) + setConnectCounter(prevCount => prevCount + 1) + setTestScanDevicesState('DONE') + setDeviceId(device.id) + } + }, + [deviceTimeService] + ) + } + + const connectAndDiscoverOnDeviceFound = async (device: Device) => { + if (checkDeviceName(device)) { + setTestScanDevicesState('DONE') + setDeviceId(device.id) + try { + for (let i = 0; i < NUMBER_OF_CALLS_IN_THE_TEST_SCENARIO; i += 1) { + console.info(`connecting to ${device.id}`) + await startConnectToDevice(device) + setConnectCounter(prevCount => prevCount + 1) + console.info(`discovering in ${device.id}`) + await startDiscoverServices() + setCharacteristicDiscoverCounter(prevCount => prevCount + 1) + } + console.info('Multiple connect success') + } catch (error) { + console.error('Multiple connect error') + } + } + } + const connectAndDisconnectOnDeviceFound = async (device: Device) => { + if (checkDeviceName(device)) { + setTestScanDevicesState('DONE') + setDeviceId(device.id) + try { + for (let i = 0; i < NUMBER_OF_CALLS_IN_THE_TEST_SCENARIO; i += 1) { + await startConnectToDevice(device) + console.info(`connecting to ${device.id}`) + setConnectInDisconnectTestCounter(prevCount => prevCount + 1) + await startDisconnect(device) + console.info(`disconnecting from ${device.id}`) + setDisconnectCounter(prevCount => prevCount + 1) + } + console.info('connect/disconnect success') + } catch (error) { + console.error('Connect/disconnect error') + } + } + } + + const discoverCharacteristicsOnly = async () => { + if (!deviceId) { + console.error('Device not ready') + return + } + try { + for (let i = 0; i < NUMBER_OF_CALLS_IN_THE_TEST_SCENARIO; i += 1) { + console.info(`discovering in ${deviceId}`) + await startDiscoverServices() + setCharacteristicDiscoverCounter(prevCount => prevCount + 1) + } + console.info('Multiple discovering success') + } catch (error) { + console.error('Multiple discovering error') + } + } + + const startConnectToDevice = (device: Device) => BLEService.connectToDevice(device.id) + + const startDiscoverServices = () => BLEService.discoverAllServicesAndCharacteristicsForDevice() + + const startDisconnect = (device: Device) => BLEService.disconnectDeviceById(device.id) + + const startCharacteristicMonitor = (directDeviceId?: DeviceId) => { + if (!deviceId && !directDeviceId) { + console.error('Device not ready') + return + } + monitorSubscriptionRef.current = BLEService.setupCustomMonitor( + directDeviceId || deviceId, + deviceTimeService, + currentTimeCharacteristic, + characteristicListener + ) + } + + const characteristicListener = (error: BleError | null, characteristic: Characteristic | null) => { + if (error) { + if (error.errorCode === BleErrorCode.ServiceNotFound || error.errorCode === BleErrorCode.ServicesNotDiscovered) { + startDiscoverServices().then(() => startCharacteristicMonitor()) + return + } + console.error(JSON.stringify(error)) + } + if (characteristic) { + if (characteristic.value) { + const message = base64.decode(characteristic.value) + console.info(message) + addMonitorMessage(message) + } + } + } + + const setupOnDeviceDisconnected = (directDeviceId?: DeviceId) => { + if (!deviceId && !directDeviceId) { + console.error('Device not ready') + return + } + BLEService.onDeviceDisconnectedCustom(directDeviceId || deviceId, disconnectedListener) + } + + const disconnectedListener = (error: BleError | null, device: Device | null) => { + if (error) { + console.error('onDeviceDisconnected') + console.error(JSON.stringify(error, null, 4)) + } + if (device) { + console.info(JSON.stringify(device, null, 4)) + } + } + + // https://github.com/dotintent/react-native-ble-plx/issues/1103 + const showIssue1103Crash = async () => { + setTestScanDevicesState('IN_PROGRESS') + await BLEService.initializeBLE() + await BLEService.scanDevices( + async (device: Device) => { + if (checkDeviceName(device)) { + console.info(`connecting to ${device.id}`) + await startConnectToDevice(device) + setConnectCounter(prevCount => prevCount + 1) + setTestScanDevicesState('DONE') + setDeviceId(device.id) + await startDiscoverServices() + await wait(1000) + setupOnDeviceDisconnected(device.id) + await wait(1000) + startCharacteristicMonitor(device.id) + await wait(1000) + const info = 'Now disconnect device' + console.info(info) + Toast.show({ + type: 'info', + text1: info + }) + } + }, + [deviceTimeService] + ) + } + + return ( + + + + + + setupOnDeviceDisconnected()} /> + + + + + + + + + startCharacteristicMonitor()} /> + + + + + ) +} diff --git a/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx b/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx index 1784f1a9..1b3715b3 100644 --- a/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx +++ b/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx @@ -1,27 +1,26 @@ import React, { useState, type Dispatch } from 'react' -import type { TestStateType } from 'example/types' import type { NativeStackScreenProps } from '@react-navigation/native-stack' -import { Device, fullUUID, type Base64 } from 'react-native-ble-plx' +import { Device, type Base64 } from 'react-native-ble-plx' import { Platform, ScrollView } from 'react-native' import base64 from 'react-native-base64' -import { getDateAsBase64 } from '../../../utils/getDateAsBase64' +import type { TestStateType } from '../../../types' import { BLEService } from '../../../services' import type { MainStackParamList } from '../../../navigation/navigators' import { AppButton, AppTextInput, ScreenDefaultContainer, TestStateDisplay } from '../../../components/atoms' import { wait } from '../../../utils/wait' +import { + currentTimeCharacteristic, + currentTimeCharacteristicTimeTriggerDescriptor, + currentTimeCharacteristicTimeTriggerDescriptorValue, + deviceTimeCharacteristic, + deviceTimeService, + monitorExpectedMessage, + writeWithResponseBase64Time, + writeWithoutResponseBase64Time +} from '../../../consts/nRFDeviceConsts' type DevicenRFTestScreenProps = NativeStackScreenProps -const deviceTimeService = fullUUID('1847') -const currentTimeCharacteristic = fullUUID('2A2B') -const deviceTimeCharacteristic = fullUUID('2B90') -const currentTimeCharacteristicTimeTriggerDescriptor = fullUUID('290E') - -const writeWithResponseBase64Time = getDateAsBase64(new Date('2022-08-11T08:17:19Z')) -const writeWithoutResponseBase64Time = getDateAsBase64(new Date('2023-09-12T10:12:16Z')) -const monitorExpectedMessage = 'Hi, it works!' -const currentTimeCharacteristicTimeTriggerDescriptorValue = base64.encode('BLE-PLX') - export function DevicenRFTestScreen(_props: DevicenRFTestScreenProps) { const [expectedDeviceName, setExpectedDeviceName] = useState('') const [testScanDevicesState, setTestScanDevicesState] = useState('WAITING') diff --git a/example/src/screens/MainStack/index.ts b/example/src/screens/MainStack/index.ts index afb59930..20784153 100644 --- a/example/src/screens/MainStack/index.ts +++ b/example/src/screens/MainStack/index.ts @@ -1,3 +1,4 @@ export * from './DashboardScreen/DashboardScreen' export * from './DeviceDetailsScreen/DeviceDetailsScreen' export * from './DevicenRFTestScreen/DevicenRFTestScreen' +export * from './DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen' diff --git a/example/src/services/BLEService/BLEService.ts b/example/src/services/BLEService/BLEService.ts index 6775c705..2f175525 100644 --- a/example/src/services/BLEService/BLEService.ts +++ b/example/src/services/BLEService/BLEService.ts @@ -5,6 +5,7 @@ import { Device, State as BluetoothState, LogLevel, + type DeviceId, type TransactionId, type UUID, type Characteristic, @@ -77,6 +78,16 @@ class BLEServiceInstance { }) } + disconnectDeviceById = (id: DeviceId) => + this.manager + .cancelDeviceConnection(id) + .then(() => this.showSuccessToast('Device disconnected')) + .catch(error => { + if (error?.code !== BleErrorCode.DeviceDisconnected) { + this.onError(error) + } + }) + onBluetoothPowerOff = () => { this.showErrorToast('Bluetooth is turned off') } @@ -95,7 +106,7 @@ class BLEServiceInstance { }) } - connectToDevice = (deviceId: string) => + connectToDevice = (deviceId: DeviceId) => new Promise((resolve, reject) => { this.manager.stopDeviceScan() this.manager @@ -211,6 +222,9 @@ class BLEServiceInstance { ) } + setupCustomMonitor: BleManager['monitorCharacteristicForDevice'] = (...args) => + this.manager.monitorCharacteristicForDevice(...args) + finishMonitor = () => { this.isCharacteristicMonitorDisconnectExpected = true this.characteristicMonitor?.remove() @@ -255,7 +269,7 @@ class BLEServiceInstance { }) } - getCharacteristicsForDevice = (serviceUUID: string) => { + getCharacteristicsForDevice = (serviceUUID: UUID) => { if (!this.device) { this.showErrorToast(deviceNotConnectedErrorText) throw new Error(deviceNotConnectedErrorText) @@ -265,7 +279,7 @@ class BLEServiceInstance { }) } - getDescriptorsForDevice = (serviceUUID: string, characteristicUUID: string) => { + getDescriptorsForDevice = (serviceUUID: UUID, characteristicUUID: UUID) => { if (!this.device) { this.showErrorToast(deviceNotConnectedErrorText) throw new Error(deviceNotConnectedErrorText) @@ -280,11 +294,11 @@ class BLEServiceInstance { this.showErrorToast(deviceNotConnectedErrorText) throw new Error(deviceNotConnectedErrorText) } - return this.manager.isDeviceConnected(this.device.id).catch(error => { - this.onError(error) - }) + return this.manager.isDeviceConnected(this.device.id) } + isDeviceWithIdConnected = (id: DeviceId) => this.manager.isDeviceConnected(id).catch(console.error) + getConnectedDevices = (expectedServices: UUID[]) => { if (!this.device) { this.showErrorToast(deviceNotConnectedErrorText) @@ -313,6 +327,9 @@ class BLEServiceInstance { return this.manager.onDeviceDisconnected(this.device.id, listener) } + onDeviceDisconnectedCustom: BleManager['onDeviceDisconnected'] = (...args) => + this.manager.onDeviceDisconnected(...args) + readRSSIForDevice = () => { if (!this.device) { this.showErrorToast(deviceNotConnectedErrorText) @@ -333,7 +350,7 @@ class BLEServiceInstance { }) } - cancelTransaction = (transactionId: string) => this.manager.cancelTransaction(transactionId) + cancelTransaction = (transactionId: TransactionId) => this.manager.cancelTransaction(transactionId) enable = () => this.manager.enable().catch(error => { diff --git a/example/types/TestStateType.ts b/example/src/types/TestStateType.ts similarity index 100% rename from example/types/TestStateType.ts rename to example/src/types/TestStateType.ts diff --git a/example/types/index.ts b/example/src/types/index.ts similarity index 100% rename from example/types/index.ts rename to example/src/types/index.ts diff --git a/src/BleManager.js b/src/BleManager.js index cc401555..9f16a5b1 100644 --- a/src/BleManager.js +++ b/src/BleManager.js @@ -28,6 +28,7 @@ import type { ConnectionOptions, BleManagerOptions } from './TypeDefinition' +import { Platform } from 'react-native' const enableDisableDeprecatedMessage = 'react-native-ble-plx: The enable and disable feature is no longer supported. In Android SDK 31+ there were major changes in permissions, which may cause problems with these functions, and in SDK 33+ they were completely removed.' @@ -457,6 +458,9 @@ export class BleManager { * @returns {Promise} Connected {@link Device} object if successful. */ async connectToDevice(deviceIdentifier: DeviceId, options: ?ConnectionOptions): Promise { + if (Platform.OS === 'android' && (await this.isDeviceConnected(deviceIdentifier))) { + await this.cancelDeviceConnection(deviceIdentifier) + } const nativeDevice = await this._callPromise(BleModule.connectToDevice(deviceIdentifier, options)) return new Device(nativeDevice, this) } From 6c6bfe2956f7573782866d48fd2547345699f7c7 Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski <97128556+gmiszewski-intent@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:23:05 +0200 Subject: [PATCH 6/8] fix: Resolves #1052 - Not a valid Bluetooth Address (#1112) --- android/src/main/java/com/bleplx/adapter/BleException.java | 4 ---- android/src/main/java/com/bleplx/adapter/BleModule.java | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 android/src/main/java/com/bleplx/adapter/BleException.java diff --git a/android/src/main/java/com/bleplx/adapter/BleException.java b/android/src/main/java/com/bleplx/adapter/BleException.java deleted file mode 100644 index 3f470b32..00000000 --- a/android/src/main/java/com/bleplx/adapter/BleException.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bleplx.adapter; - -public interface BleException { -} diff --git a/android/src/main/java/com/bleplx/adapter/BleModule.java b/android/src/main/java/com/bleplx/adapter/BleModule.java index 4c2cf8f5..4cba8f64 100755 --- a/android/src/main/java/com/bleplx/adapter/BleModule.java +++ b/android/src/main/java/com/bleplx/adapter/BleModule.java @@ -445,6 +445,7 @@ public void isDeviceConnected(String deviceIdentifier, .equals(RxBleConnection.RxBleConnectionState.CONNECTED); onSuccessCallback.onSuccess(connected); } catch (Exception e) { + RxBleLog.e(e, "Error while checking if device is connected"); onErrorCallback.onError(errorConverter.toError(e)); } } From fe7f5afc73b40bcb6b2525f4b5c5afdc46c24372 Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski <97128556+gmiszewski-intent@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:40:41 +0200 Subject: [PATCH 7/8] feat: provide option to disable legacy scan for Android (#1118) --- android/src/main/java/com/bleplx/BlePlxModule.java | 6 +++++- .../src/main/java/com/bleplx/adapter/BleAdapter.java | 1 + .../src/main/java/com/bleplx/adapter/BleModule.java | 7 +++++-- .../src/components/atoms/Button/Button.styled.tsx | 1 + .../MainStack/DashboardScreen/DashboardScreen.tsx | 12 +++++++++++- example/src/services/BLEService/BLEService.ts | 4 ++-- src/index.d.ts | 5 +++++ 7 files changed, 30 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/com/bleplx/BlePlxModule.java b/android/src/main/java/com/bleplx/BlePlxModule.java index b165a1b3..1a415025 100644 --- a/android/src/main/java/com/bleplx/BlePlxModule.java +++ b/android/src/main/java/com/bleplx/BlePlxModule.java @@ -188,6 +188,7 @@ public void startDeviceScan(@Nullable ReadableArray filteredUUIDs, @Nullable Rea int scanMode = DEFAULT_SCAN_MODE_LOW_POWER; int callbackType = DEFAULT_CALLBACK_TYPE_ALL_MATCHES; + boolean legacyScan = true; if (options != null) { if (options.hasKey("scanMode") && options.getType("scanMode") == ReadableType.Number) { @@ -196,11 +197,14 @@ public void startDeviceScan(@Nullable ReadableArray filteredUUIDs, @Nullable Rea if (options.hasKey("callbackType") && options.getType("callbackType") == ReadableType.Number) { callbackType = options.getInt("callbackType"); } + if (options.hasKey("legacyScan") && options.getType("legacyScan") == ReadableType.Boolean) { + legacyScan = options.getBoolean("legacyScan"); + } } bleAdapter.startDeviceScan( filteredUUIDs != null ? ReadableArrayConverter.toStringArray(filteredUUIDs) : null, - scanMode, callbackType, + scanMode, callbackType, legacyScan, new OnEventCallback() { @Override public void onEvent(ScanResult data) { diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapter.java b/android/src/main/java/com/bleplx/adapter/BleAdapter.java index 9d63f3bd..e0502ae7 100644 --- a/android/src/main/java/com/bleplx/adapter/BleAdapter.java +++ b/android/src/main/java/com/bleplx/adapter/BleAdapter.java @@ -28,6 +28,7 @@ void startDeviceScan( String[] filteredUUIDs, int scanMode, int callbackType, + boolean legacyScan, OnEventCallback onEventCallback, OnErrorCallback onErrorCallback); diff --git a/android/src/main/java/com/bleplx/adapter/BleModule.java b/android/src/main/java/com/bleplx/adapter/BleModule.java index 4cba8f64..1c46cb51 100755 --- a/android/src/main/java/com/bleplx/adapter/BleModule.java +++ b/android/src/main/java/com/bleplx/adapter/BleModule.java @@ -177,6 +177,7 @@ public String getCurrentState() { public void startDeviceScan(String[] filteredUUIDs, int scanMode, int callbackType, + boolean legacyScan, OnEventCallback onEventCallback, OnErrorCallback onErrorCallback) { UUID[] uuids = null; @@ -189,7 +190,7 @@ public void startDeviceScan(String[] filteredUUIDs, } } - safeStartDeviceScan(uuids, scanMode, callbackType, onEventCallback, onErrorCallback); + safeStartDeviceScan(uuids, scanMode, callbackType, legacyScan, onEventCallback, onErrorCallback); } @Override @@ -1159,7 +1160,8 @@ private String mapNativeAdapterStateToLocalBluetoothState(int adapterState) { private void safeStartDeviceScan(final UUID[] uuids, final int scanMode, - int callbackType, + final int callbackType, + final boolean legacyScan, final OnEventCallback onEventCallback, final OnErrorCallback onErrorCallback) { if (rxBleClient == null) { @@ -1170,6 +1172,7 @@ private void safeStartDeviceScan(final UUID[] uuids, ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(scanMode) .setCallbackType(callbackType) + .setLegacy(legacyScan) .build(); int length = uuids == null ? 0 : uuids.length; diff --git a/example/src/components/atoms/Button/Button.styled.tsx b/example/src/components/atoms/Button/Button.styled.tsx index c8cc1f6b..73a322dc 100644 --- a/example/src/components/atoms/Button/Button.styled.tsx +++ b/example/src/components/atoms/Button/Button.styled.tsx @@ -5,6 +5,7 @@ import { AppText } from '../AppText/AppText' export const Container = styled(TouchableOpacity)` ${({ theme }) => css` background-color: ${theme.colors.mainRed}; + margin: 10px; padding: 12px; align-items: center; border-radius: 100px; diff --git a/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx b/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx index 0fc6c65f..f59c86c7 100644 --- a/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx +++ b/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx @@ -75,7 +75,17 @@ export function DashboardScreen({ navigation }: DashboardScreenProps) { )} BLEService.initializeBLE().then(() => BLEService.scanDevices(addFoundDevice))} + onPress={() => { + setFoundDevices([]) + BLEService.initializeBLE().then(() => BLEService.scanDevices(addFoundDevice, null, true)) + }} + /> + { + setFoundDevices([]) + BLEService.initializeBLE().then(() => BLEService.scanDevices(addFoundDevice, null, false)) + }} /> navigation.navigate('DEVICE_NRF_TEST_SCREEN')} /> diff --git a/example/src/services/BLEService/BLEService.ts b/example/src/services/BLEService/BLEService.ts index 2f175525..35fdd94a 100644 --- a/example/src/services/BLEService/BLEService.ts +++ b/example/src/services/BLEService/BLEService.ts @@ -92,8 +92,8 @@ class BLEServiceInstance { this.showErrorToast('Bluetooth is turned off') } - scanDevices = async (onDeviceFound: (device: Device) => void, UUIDs: UUID[] | null = null) => { - this.manager.startDeviceScan(UUIDs, null, (error, device) => { + scanDevices = async (onDeviceFound: (device: Device) => void, UUIDs: UUID[] | null = null, legacyScan?: boolean) => { + this.manager.startDeviceScan(UUIDs, { legacyScan }, (error, device) => { if (error) { this.onError(error) console.error(error.message) diff --git a/src/index.d.ts b/src/index.d.ts index f6e460d3..c313d5dc 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -162,6 +162,11 @@ declare module 'react-native-ble-plx' { * Scan callback type for Bluetooth LE scan [Android only] */ callbackType?: ScanCallbackType + /** + * Use legacyScan (default true) [Android only] + * https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder#setLegacy(boolean) + */ + legacyScan?: boolean } /** From 1560d5e54bd877409683c19edd272623a208103d Mon Sep 17 00:00:00 2001 From: Grzegorz Miszewski <97128556+gmiszewski-intent@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:32:39 +0200 Subject: [PATCH 8/8] chore: use Android Studio default formatting (#1120) --- android/src/main/AndroidManifest.xml | 4 +- android/src/main/AndroidManifestNew.xml | 3 +- .../main/java/com/bleplx/BlePlxModule.java | 1791 ++++++----- android/src/main/java/com/bleplx/Event.java | 18 +- .../com/bleplx/adapter/AdvertisementData.java | 469 +-- .../java/com/bleplx/adapter/BleAdapter.java | 484 +-- .../com/bleplx/adapter/BleAdapterCreator.java | 2 +- .../com/bleplx/adapter/BleAdapterFactory.java | 24 +- .../java/com/bleplx/adapter/BleModule.java | 2612 ++++++++--------- .../com/bleplx/adapter/Characteristic.java | 262 +- .../com/bleplx/adapter/ConnectionOptions.java | 106 +- .../com/bleplx/adapter/ConnectionState.java | 10 +- .../java/com/bleplx/adapter/Descriptor.java | 200 +- .../main/java/com/bleplx/adapter/Device.java | 132 +- .../com/bleplx/adapter/OnErrorCallback.java | 2 +- .../com/bleplx/adapter/OnEventCallback.java | 2 +- .../com/bleplx/adapter/OnSuccessCallback.java | 2 +- .../com/bleplx/adapter/RefreshGattMoment.java | 20 +- .../java/com/bleplx/adapter/ScanResult.java | 234 +- .../main/java/com/bleplx/adapter/Service.java | 82 +- .../com/bleplx/adapter/errors/BleError.java | 48 +- .../bleplx/adapter/errors/BleErrorCode.java | 102 +- .../bleplx/adapter/errors/BleErrorUtils.java | 152 +- .../bleplx/adapter/errors/ErrorConverter.java | 380 +-- .../CannotMonitorCharacteristicException.java | 14 +- .../bleplx/adapter/utils/Base64Converter.java | 13 +- .../com/bleplx/adapter/utils/ByteUtils.java | 18 +- .../com/bleplx/adapter/utils/Constants.java | 94 +- .../bleplx/adapter/utils/DisposableMap.java | 44 +- .../com/bleplx/adapter/utils/IdGenerator.java | 26 +- .../bleplx/adapter/utils/IdGeneratorKey.java | 58 +- .../com/bleplx/adapter/utils/LogLevel.java | 72 +- .../utils/RefreshGattCustomOperation.java | 60 +- .../bleplx/adapter/utils/SafeExecutor.java | 34 +- .../bleplx/adapter/utils/ServiceFactory.java | 14 +- .../bleplx/adapter/utils/UUIDConverter.java | 76 +- .../mapper/RxBleDeviceToDeviceMapper.java | 12 +- .../BleErrorToJsObjectConverter.java | 93 +- .../CharacteristicToJsObjectConverter.java | 70 +- .../DescriptorToJsObjectConverter.java | 54 +- .../converter/DeviceToJsObjectConverter.java | 89 +- .../bleplx/converter/JSObjectConverter.java | 16 +- .../ScanResultToJsObjectConverter.java | 142 +- .../converter/ServiceToJsObjectConverter.java | 34 +- .../com/bleplx/utils/Base64Converter.java | 13 +- .../bleplx/utils/ReadableArrayConverter.java | 12 +- .../java/com/bleplx/utils/SafePromise.java | 60 +- .../java/com/bleplx/utils/UUIDConverter.java | 98 +- 48 files changed, 4181 insertions(+), 4176 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 8854d00e..e418b916 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.bleplx" + xmlns:tools="http://schemas.android.com/tools"> diff --git a/android/src/main/AndroidManifestNew.xml b/android/src/main/AndroidManifestNew.xml index a2f47b60..0bfa73ba 100644 --- a/android/src/main/AndroidManifestNew.xml +++ b/android/src/main/AndroidManifestNew.xml @@ -1,2 +1 @@ - - + diff --git a/android/src/main/java/com/bleplx/BlePlxModule.java b/android/src/main/java/com/bleplx/BlePlxModule.java index 1a415025..ca1d171c 100644 --- a/android/src/main/java/com/bleplx/BlePlxModule.java +++ b/android/src/main/java/com/bleplx/BlePlxModule.java @@ -1,19 +1,10 @@ package com.bleplx; +import android.app.Activity; + import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableType; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.modules.core.DeviceEventManagerModule; import com.bleplx.adapter.BleAdapter; import com.bleplx.adapter.BleAdapterFactory; import com.bleplx.adapter.Characteristic; @@ -36,16 +27,24 @@ import com.bleplx.converter.ServiceToJsObjectConverter; import com.bleplx.utils.ReadableArrayConverter; import com.bleplx.utils.SafePromise; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableType; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.modules.core.DeviceEventManagerModule; import com.polidea.rxandroidble2.internal.RxBleLog; import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.annotation.Nullable; - -import android.app.Activity; - import io.reactivex.exceptions.UndeliverableException; import io.reactivex.plugins.RxJavaPlugins; @@ -53,918 +52,918 @@ public class BlePlxModule extends ReactContextBaseJavaModule { public static final String NAME = "BlePlx"; - public BlePlxModule(ReactApplicationContext reactContext) { - super(reactContext); - RxJavaPlugins.setErrorHandler(throwable -> { - if (throwable instanceof UndeliverableException) { - RxBleLog.e("Handle all unhandled exceptions from RxJava: " + throwable.getMessage()); - } else { - Thread currentThread = Thread.currentThread(); - Thread.UncaughtExceptionHandler errorHandler = currentThread.getUncaughtExceptionHandler(); - if (errorHandler != null) { - errorHandler.uncaughtException(currentThread, throwable); + public BlePlxModule(ReactApplicationContext reactContext) { + super(reactContext); + RxJavaPlugins.setErrorHandler(throwable -> { + if (throwable instanceof UndeliverableException) { + RxBleLog.e("Handle all unhandled exceptions from RxJava: " + throwable.getMessage()); + } else { + Thread currentThread = Thread.currentThread(); + Thread.UncaughtExceptionHandler errorHandler = currentThread.getUncaughtExceptionHandler(); + if (errorHandler != null) { + errorHandler.uncaughtException(currentThread, throwable); + } + } + }); + } + + @Override + @NonNull + public String getName() { + return NAME; + } + + + // Value converters + private final BleErrorToJsObjectConverter errorConverter = new BleErrorToJsObjectConverter(); + private final ScanResultToJsObjectConverter scanResultConverter = new ScanResultToJsObjectConverter(); + private final DeviceToJsObjectConverter deviceConverter = new DeviceToJsObjectConverter(); + private final CharacteristicToJsObjectConverter characteristicConverter = new CharacteristicToJsObjectConverter(); + private final DescriptorToJsObjectConverter descriptorConverter = new DescriptorToJsObjectConverter(); + private final ServiceToJsObjectConverter serviceConverter = new ServiceToJsObjectConverter(); + + private BleAdapter bleAdapter; + + @Override + public Map getConstants() { + final Map constants = new HashMap<>(); + for (Event event : Event.values()) { + constants.put(event.name, event.name); + } + return constants; + } + + // Lifecycle ----------------------------------------------------------------------------------- + + @ReactMethod + public void createClient(String restoreStateIdentifier) { + final Activity activity = getCurrentActivity(); + if (activity == null) { + return; + } + bleAdapter = BleAdapterFactory.getNewAdapter(activity); + bleAdapter.createClient(restoreStateIdentifier, + new OnEventCallback() { + @Override + public void onEvent(String state) { + sendEvent(Event.StateChangeEvent, state); + } + }, new OnEventCallback() { + @Override + public void onEvent(Integer data) { + sendEvent(Event.RestoreStateEvent, null); + } + }); + } + + @ReactMethod + public void destroyClient() { + bleAdapter.destroyClient(); + bleAdapter = null; + } + + // Mark: Common -------------------------------------------------------------------------------- + + @ReactMethod + public void cancelTransaction(String transactionId) { + bleAdapter.cancelTransaction(transactionId); + } + + @ReactMethod + public void setLogLevel(String logLevel) { + bleAdapter.setLogLevel(logLevel); + } + + @ReactMethod + public void logLevel(Promise promise) { + promise.resolve(bleAdapter.getLogLevel()); + } + + // Mark: Monitoring state ---------------------------------------------------------------------- + + @ReactMethod + public void enable(final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.enable(transactionId, new OnSuccessCallback() { + @Override + public void onSuccess(Void data) { + safePromise.resolve(null); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + @ReactMethod + public void disable(final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.disable(transactionId, new OnSuccessCallback() { + @Override + public void onSuccess(Void data) { + safePromise.resolve(null); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + @ReactMethod + public void state(Promise promise) { + promise.resolve(bleAdapter.getCurrentState()); + } + + // Mark: Scanning ------------------------------------------------------------------------------ + + @ReactMethod + public void startDeviceScan(@Nullable ReadableArray filteredUUIDs, @Nullable ReadableMap options) { + final int DEFAULT_SCAN_MODE_LOW_POWER = 0; + final int DEFAULT_CALLBACK_TYPE_ALL_MATCHES = 1; + + int scanMode = DEFAULT_SCAN_MODE_LOW_POWER; + int callbackType = DEFAULT_CALLBACK_TYPE_ALL_MATCHES; + boolean legacyScan = true; + + if (options != null) { + if (options.hasKey("scanMode") && options.getType("scanMode") == ReadableType.Number) { + scanMode = options.getInt("scanMode"); + } + if (options.hasKey("callbackType") && options.getType("callbackType") == ReadableType.Number) { + callbackType = options.getInt("callbackType"); + } + if (options.hasKey("legacyScan") && options.getType("legacyScan") == ReadableType.Boolean) { + legacyScan = options.getBoolean("legacyScan"); + } + } + + bleAdapter.startDeviceScan( + filteredUUIDs != null ? ReadableArrayConverter.toStringArray(filteredUUIDs) : null, + scanMode, callbackType, legacyScan, + new OnEventCallback() { + @Override + public void onEvent(ScanResult data) { + sendEvent(Event.ScanEvent, scanResultConverter.toJSCallback(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + sendEvent(Event.ScanEvent, errorConverter.toJSCallback(error)); + } + }); + } + + @ReactMethod + public void stopDeviceScan() { + bleAdapter.stopDeviceScan(); + } + + // Mark: Device management --------------------------------------------------------------------- + + @ReactMethod + public void devices(final ReadableArray deviceIdentifiers, final Promise promise) { + bleAdapter.getKnownDevices(ReadableArrayConverter.toStringArray(deviceIdentifiers), + new OnSuccessCallback() { + @Override + public void onSuccess(Device[] data) { + WritableArray jsDevices = Arguments.createArray(); + for (Device device : data) { + jsDevices.pushMap(deviceConverter.toJSObject(device)); } + promise.resolve(jsDevices); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + promise.reject(null, errorConverter.toJs(error)); } }); - } - - @Override - @NonNull - public String getName() { - return NAME; - } - - - // Value converters - private final BleErrorToJsObjectConverter errorConverter = new BleErrorToJsObjectConverter(); - private final ScanResultToJsObjectConverter scanResultConverter = new ScanResultToJsObjectConverter(); - private final DeviceToJsObjectConverter deviceConverter = new DeviceToJsObjectConverter(); - private final CharacteristicToJsObjectConverter characteristicConverter = new CharacteristicToJsObjectConverter(); - private final DescriptorToJsObjectConverter descriptorConverter = new DescriptorToJsObjectConverter(); - private final ServiceToJsObjectConverter serviceConverter = new ServiceToJsObjectConverter(); - - private BleAdapter bleAdapter; - - @Override - public Map getConstants() { - final Map constants = new HashMap<>(); - for (Event event : Event.values()) { - constants.put(event.name, event.name); + } + + @ReactMethod + public void connectedDevices(final ReadableArray serviceUUIDs, final Promise promise) { + bleAdapter.getConnectedDevices(ReadableArrayConverter.toStringArray(serviceUUIDs), + new OnSuccessCallback() { + @Override + public void onSuccess(Device[] data) { + final WritableArray writableArray = Arguments.createArray(); + for (Device device : data) { + writableArray.pushMap(deviceConverter.toJSObject(device)); + } + promise.resolve(writableArray); } - return constants; - } - - // Lifecycle ----------------------------------------------------------------------------------- - - @ReactMethod - public void createClient(String restoreStateIdentifier) { - final Activity activity = getCurrentActivity(); - if (activity == null) { - return; - } - bleAdapter = BleAdapterFactory.getNewAdapter(activity); - bleAdapter.createClient(restoreStateIdentifier, - new OnEventCallback() { - @Override - public void onEvent(String state) { - sendEvent(Event.StateChangeEvent, state); - } - }, new OnEventCallback() { - @Override - public void onEvent(Integer data) { - sendEvent(Event.RestoreStateEvent, null); - } - }); - } - - @ReactMethod - public void destroyClient() { - bleAdapter.destroyClient(); - bleAdapter = null; - } - - // Mark: Common -------------------------------------------------------------------------------- - - @ReactMethod - public void cancelTransaction(String transactionId) { - bleAdapter.cancelTransaction(transactionId); - } - - @ReactMethod - public void setLogLevel(String logLevel) { - bleAdapter.setLogLevel(logLevel); - } - - @ReactMethod - public void logLevel(Promise promise) { - promise.resolve(bleAdapter.getLogLevel()); - } - - // Mark: Monitoring state ---------------------------------------------------------------------- - - @ReactMethod - public void enable(final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.enable(transactionId, new OnSuccessCallback() { - @Override - public void onSuccess(Void data) { - safePromise.resolve(null); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void disable(final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.disable(transactionId, new OnSuccessCallback() { - @Override - public void onSuccess(Void data) { - safePromise.resolve(null); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void state(Promise promise) { - promise.resolve(bleAdapter.getCurrentState()); - } - - // Mark: Scanning ------------------------------------------------------------------------------ - - @ReactMethod - public void startDeviceScan(@Nullable ReadableArray filteredUUIDs, @Nullable ReadableMap options) { - final int DEFAULT_SCAN_MODE_LOW_POWER = 0; - final int DEFAULT_CALLBACK_TYPE_ALL_MATCHES = 1; - - int scanMode = DEFAULT_SCAN_MODE_LOW_POWER; - int callbackType = DEFAULT_CALLBACK_TYPE_ALL_MATCHES; - boolean legacyScan = true; - - if (options != null) { - if (options.hasKey("scanMode") && options.getType("scanMode") == ReadableType.Number) { - scanMode = options.getInt("scanMode"); - } - if (options.hasKey("callbackType") && options.getType("callbackType") == ReadableType.Number) { - callbackType = options.getInt("callbackType"); - } - if (options.hasKey("legacyScan") && options.getType("legacyScan") == ReadableType.Boolean) { - legacyScan = options.getBoolean("legacyScan"); + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + }); + } + + // Mark: Device operations --------------------------------------------------------------------- + + @ReactMethod + public void requestConnectionPriorityForDevice(final String deviceId, int connectionPriority, final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.requestConnectionPriorityForDevice(deviceId, connectionPriority, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + @ReactMethod + public void requestMTUForDevice(final String deviceId, int mtu, final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.requestMTUForDevice(deviceId, mtu, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + @ReactMethod + public void readRSSIForDevice(final String deviceId, final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.readRSSIForDevice(deviceId, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + @ReactMethod + public void connectToDevice(final String deviceId, @Nullable ReadableMap options, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + boolean autoConnect = false; + int requestMtu = 0; + RefreshGattMoment refreshGattMoment = null; + Integer timeout = null; + int connectionPriority = 0; // CONNECTION_PRIORITY_BALANCED + + if (options != null) { + if (options.hasKey("autoConnect") && options.getType("autoConnect") == ReadableType.Boolean) { + autoConnect = options.getBoolean("autoConnect"); + } + if (options.hasKey("requestMTU") && options.getType("requestMTU") == ReadableType.Number) { + requestMtu = options.getInt("requestMTU"); + } + if (options.hasKey("refreshGatt") && options.getType("refreshGatt") == ReadableType.String) { + refreshGattMoment = RefreshGattMoment.getByName(options.getString("refreshGatt")); + } + if (options.hasKey("timeout") && options.getType("timeout") == ReadableType.Number) { + timeout = options.getInt("timeout"); + } + if (options.hasKey("connectionPriority") && options.getType("connectionPriority") == ReadableType.Number) { + connectionPriority = options.getInt("connectionPriority"); + } + } + bleAdapter.connectToDevice( + deviceId, + new ConnectionOptions(autoConnect, + requestMtu, + refreshGattMoment, + timeout != null ? timeout.longValue() : null, + connectionPriority), + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); + } + }, + new OnEventCallback() { + @Override + public void onEvent(ConnectionState connectionState) { + if (connectionState == ConnectionState.DISCONNECTED) { + WritableArray event = Arguments.createArray(); + event.pushNull(); + WritableMap device = Arguments.createMap(); + device.putString("id", deviceId); + event.pushMap(device); + sendEvent(Event.DisconnectionEvent, event); } } - - bleAdapter.startDeviceScan( - filteredUUIDs != null ? ReadableArrayConverter.toStringArray(filteredUUIDs) : null, - scanMode, callbackType, legacyScan, - new OnEventCallback() { - @Override - public void onEvent(ScanResult data) { - sendEvent(Event.ScanEvent, scanResultConverter.toJSCallback(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - sendEvent(Event.ScanEvent, errorConverter.toJSCallback(error)); - } - }); - } - - @ReactMethod - public void stopDeviceScan() { - bleAdapter.stopDeviceScan(); - } - - // Mark: Device management --------------------------------------------------------------------- - - @ReactMethod - public void devices(final ReadableArray deviceIdentifiers, final Promise promise) { - bleAdapter.getKnownDevices(ReadableArrayConverter.toStringArray(deviceIdentifiers), - new OnSuccessCallback() { - @Override - public void onSuccess(Device[] data) { - WritableArray jsDevices = Arguments.createArray(); - for (Device device : data) { - jsDevices.pushMap(deviceConverter.toJSObject(device)); - } - promise.resolve(jsDevices); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - promise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void connectedDevices(final ReadableArray serviceUUIDs, final Promise promise) { - bleAdapter.getConnectedDevices(ReadableArrayConverter.toStringArray(serviceUUIDs), - new OnSuccessCallback() { - @Override - public void onSuccess(Device[] data) { - final WritableArray writableArray = Arguments.createArray(); - for (Device device : data) { - writableArray.pushMap(deviceConverter.toJSObject(device)); - } - promise.resolve(writableArray); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - promise.reject(null, errorConverter.toJs(error)); - } - }); - } - - // Mark: Device operations --------------------------------------------------------------------- - - @ReactMethod - public void requestConnectionPriorityForDevice(final String deviceId, int connectionPriority, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.requestConnectionPriorityForDevice(deviceId, connectionPriority, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void requestMTUForDevice(final String deviceId, int mtu, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.requestMTUForDevice(deviceId, mtu, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void readRSSIForDevice(final String deviceId, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.readRSSIForDevice(deviceId, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void connectToDevice(final String deviceId, @Nullable ReadableMap options, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - boolean autoConnect = false; - int requestMtu = 0; - RefreshGattMoment refreshGattMoment = null; - Integer timeout = null; - int connectionPriority = 0; // CONNECTION_PRIORITY_BALANCED - - if (options != null) { - if (options.hasKey("autoConnect") && options.getType("autoConnect") == ReadableType.Boolean) { - autoConnect = options.getBoolean("autoConnect"); - } - if (options.hasKey("requestMTU") && options.getType("requestMTU") == ReadableType.Number) { - requestMtu = options.getInt("requestMTU"); - } - if (options.hasKey("refreshGatt") && options.getType("refreshGatt") == ReadableType.String) { - refreshGattMoment = RefreshGattMoment.getByName(options.getString("refreshGatt")); - } - if (options.hasKey("timeout") && options.getType("timeout") == ReadableType.Number) { - timeout = options.getInt("timeout"); - } - if (options.hasKey("connectionPriority") && options.getType("connectionPriority") == ReadableType.Number) { - connectionPriority = options.getInt("connectionPriority"); - } - } - bleAdapter.connectToDevice( - deviceId, - new ConnectionOptions(autoConnect, - requestMtu, - refreshGattMoment, - timeout != null ? timeout.longValue() : null, - connectionPriority), - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, - new OnEventCallback() { - @Override - public void onEvent(ConnectionState connectionState) { - if (connectionState == ConnectionState.DISCONNECTED) { - WritableArray event = Arguments.createArray(); - event.pushNull(); - WritableMap device = Arguments.createMap(); - device.putString("id", deviceId); - event.pushMap(device); - sendEvent(Event.DisconnectionEvent, event); - } - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void cancelDeviceConnection(String deviceId, Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.cancelDeviceConnection(deviceId, - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void isDeviceConnected(String deviceId, final Promise promise) { - bleAdapter.isDeviceConnected(deviceId, - new OnSuccessCallback() { - @Override - public void onSuccess(Boolean isConnected) { - promise.resolve(isConnected); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - promise.reject(null, errorConverter.toJs(error)); - } - }); - } - - // Mark: Discovery ----------------------------------------------------------------------------- - - @ReactMethod - public void discoverAllServicesAndCharacteristicsForDevice(String deviceId, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.discoverAllServicesAndCharacteristicsForDevice(deviceId, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Device data) { - safePromise.resolve(deviceConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - // Mark: Service and characteristic getters ---------------------------------------------------- - - @ReactMethod - public void servicesForDevice(final String deviceId, final Promise promise) { - try { - List services = bleAdapter.getServicesForDevice(deviceId); - WritableArray jsArray = Arguments.createArray(); - for (Service service : services) { - jsArray.pushMap(serviceConverter.toJSObject(service)); - } - promise.resolve(jsArray); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + }, + new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); } - - } - - @ReactMethod - public void characteristicsForDevice(final String deviceId, - final String serviceUUID, - final Promise promise) { - try { - List characteristics = bleAdapter.getCharacteristicsForDevice(deviceId, serviceUUID); - - WritableArray jsCharacteristics = Arguments.createArray(); - for (Characteristic characteristic : characteristics) { - jsCharacteristics.pushMap(characteristicConverter.toJSObject(characteristic)); - } - promise.resolve(jsCharacteristics); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + }); + } + + @ReactMethod + public void cancelDeviceConnection(String deviceId, Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.cancelDeviceConnection(deviceId, + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); } - } - - @ReactMethod - public void characteristicsForService(final int serviceIdentifier, final Promise promise) { - try { - List characteristics = bleAdapter.getCharacteristicsForService(serviceIdentifier); - WritableArray jsCharacteristics = Arguments.createArray(); - for (Characteristic characteristic : characteristics) { - jsCharacteristics.pushMap(characteristicConverter.toJSObject(characteristic)); - } - promise.resolve(jsCharacteristics); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); } - } - - @ReactMethod - public void descriptorsForDevice(final String deviceIdentifier, - final String serviceUUID, - final String characteristicUUID, - final Promise promise) { - try { - List descriptors = bleAdapter.descriptorsForDevice(deviceIdentifier, serviceUUID, characteristicUUID); - WritableArray jsDescriptors = Arguments.createArray(); - for (Descriptor descriptor : descriptors) { - jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); - } - promise.resolve(jsDescriptors); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + }); + } + + @ReactMethod + public void isDeviceConnected(String deviceId, final Promise promise) { + bleAdapter.isDeviceConnected(deviceId, + new OnSuccessCallback() { + @Override + public void onSuccess(Boolean isConnected) { + promise.resolve(isConnected); } - } - - @ReactMethod - public void descriptorsForService(final int serviceIdentifier, - final String characteristicUUID, - final Promise promise) { - try { - List descriptors = bleAdapter.descriptorsForService(serviceIdentifier, characteristicUUID); - WritableArray jsDescriptors = Arguments.createArray(); - for (Descriptor descriptor : descriptors) { - jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); - } - promise.resolve(jsDescriptors); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + promise.reject(null, errorConverter.toJs(error)); } - } + }); + } + + // Mark: Discovery ----------------------------------------------------------------------------- + + @ReactMethod + public void discoverAllServicesAndCharacteristicsForDevice(String deviceId, final String transactionId, final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.discoverAllServicesAndCharacteristicsForDevice(deviceId, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Device data) { + safePromise.resolve(deviceConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } + + // Mark: Service and characteristic getters ---------------------------------------------------- + + @ReactMethod + public void servicesForDevice(final String deviceId, final Promise promise) { + try { + List services = bleAdapter.getServicesForDevice(deviceId); + WritableArray jsArray = Arguments.createArray(); + for (Service service : services) { + jsArray.pushMap(serviceConverter.toJSObject(service)); + } + promise.resolve(jsArray); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + + } + + @ReactMethod + public void characteristicsForDevice(final String deviceId, + final String serviceUUID, + final Promise promise) { + try { + List characteristics = bleAdapter.getCharacteristicsForDevice(deviceId, serviceUUID); + + WritableArray jsCharacteristics = Arguments.createArray(); + for (Characteristic characteristic : characteristics) { + jsCharacteristics.pushMap(characteristicConverter.toJSObject(characteristic)); + } + promise.resolve(jsCharacteristics); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + } + + @ReactMethod + public void characteristicsForService(final int serviceIdentifier, final Promise promise) { + try { + List characteristics = bleAdapter.getCharacteristicsForService(serviceIdentifier); + WritableArray jsCharacteristics = Arguments.createArray(); + for (Characteristic characteristic : characteristics) { + jsCharacteristics.pushMap(characteristicConverter.toJSObject(characteristic)); + } + promise.resolve(jsCharacteristics); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + } + + @ReactMethod + public void descriptorsForDevice(final String deviceIdentifier, + final String serviceUUID, + final String characteristicUUID, + final Promise promise) { + try { + List descriptors = bleAdapter.descriptorsForDevice(deviceIdentifier, serviceUUID, characteristicUUID); + WritableArray jsDescriptors = Arguments.createArray(); + for (Descriptor descriptor : descriptors) { + jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); + } + promise.resolve(jsDescriptors); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + } + + @ReactMethod + public void descriptorsForService(final int serviceIdentifier, + final String characteristicUUID, + final Promise promise) { + try { + List descriptors = bleAdapter.descriptorsForService(serviceIdentifier, characteristicUUID); + WritableArray jsDescriptors = Arguments.createArray(); + for (Descriptor descriptor : descriptors) { + jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); + } + promise.resolve(jsDescriptors); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + } + + @ReactMethod + public void descriptorsForCharacteristic(final int characteristicIdentifier, + final Promise promise) { + try { + List descriptors = bleAdapter.descriptorsForCharacteristic(characteristicIdentifier); + WritableArray jsDescriptors = Arguments.createArray(); + for (Descriptor descriptor : descriptors) { + jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); + } + promise.resolve(jsDescriptors); + } catch (BleError error) { + promise.reject(null, errorConverter.toJs(error)); + } + } + + // Mark: Characteristics operations ------------------------------------------------------------ + + @ReactMethod + public void writeCharacteristicForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String valueBase64, + final Boolean response, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + bleAdapter.writeCharacteristicForDevice( + deviceId, serviceUUID, characteristicUUID, valueBase64, response, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } - @ReactMethod - public void descriptorsForCharacteristic(final int characteristicIdentifier, - final Promise promise) { - try { - List descriptors = bleAdapter.descriptorsForCharacteristic(characteristicIdentifier); - WritableArray jsDescriptors = Arguments.createArray(); - for (Descriptor descriptor : descriptors) { - jsDescriptors.pushMap(descriptorConverter.toJSObject(descriptor)); - } - promise.resolve(jsDescriptors); - } catch (BleError error) { - promise.reject(null, errorConverter.toJs(error)); + @ReactMethod + public void writeCharacteristicForService(final int serviceIdentifier, + final String characteristicUUID, + final String valueBase64, + final Boolean response, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.writeCharacteristicForService( + serviceIdentifier, characteristicUUID, valueBase64, response, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); } - } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } + + @ReactMethod + public void writeCharacteristic(final int characteristicIdentifier, + final String valueBase64, + final Boolean response, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + bleAdapter.writeCharacteristic(characteristicIdentifier, valueBase64, response, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + }); + } - // Mark: Characteristics operations ------------------------------------------------------------ + @ReactMethod + public void readCharacteristicForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + bleAdapter.readCharacteristicForDevice( + deviceId, serviceUUID, characteristicUUID, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } + + @ReactMethod + public void readCharacteristicForService(final int serviceIdentifier, + final String characteristicUUID, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + bleAdapter.readCharacteristicForService( + serviceIdentifier, characteristicUUID, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } + + @ReactMethod + public void readCharacteristic(final int characteristicIdentifier, + final String transactionId, + final Promise promise) { + final SafePromise safePromise = new SafePromise(promise); + + bleAdapter.readCharacteristic( + characteristicIdentifier, transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Characteristic data) { + safePromise.resolve(characteristicConverter.toJSObject(data)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } - @ReactMethod - public void writeCharacteristicForDevice(final String deviceId, + @ReactMethod + public void monitorCharacteristicForDevice(final String deviceId, final String serviceUUID, final String characteristicUUID, - final String valueBase64, - final Boolean response, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - bleAdapter.writeCharacteristicForDevice( - deviceId, serviceUUID, characteristicUUID, valueBase64, response, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.monitorCharacteristicForDevice( + deviceId, serviceUUID, characteristicUUID, transactionId, + new OnEventCallback() { + @Override + public void onEvent(Characteristic data) { + WritableArray jsResult = Arguments.createArray(); + jsResult.pushNull(); + jsResult.pushMap(characteristicConverter.toJSObject(data)); + jsResult.pushString(transactionId); + sendEvent(Event.ReadEvent, jsResult); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } - @ReactMethod - public void writeCharacteristicForService(final int serviceIdentifier, + @ReactMethod + public void monitorCharacteristicForService(final int serviceIdentifier, final String characteristicUUID, - final String valueBase64, - final Boolean response, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.writeCharacteristicForService( - serviceIdentifier, characteristicUUID, valueBase64, response, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } + final SafePromise safePromise = new SafePromise(promise); + bleAdapter.monitorCharacteristicForService( + serviceIdentifier, characteristicUUID, transactionId, + new OnEventCallback() { + @Override + public void onEvent(Characteristic data) { + WritableArray jsResult = Arguments.createArray(); + jsResult.pushNull(); + jsResult.pushMap(characteristicConverter.toJSObject(data)); + jsResult.pushString(transactionId); + sendEvent(Event.ReadEvent, jsResult); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } - @ReactMethod - public void writeCharacteristic(final int characteristicIdentifier, - final String valueBase64, - final Boolean response, + @ReactMethod + public void monitorCharacteristic(final int characteristicIdentifier, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - bleAdapter.writeCharacteristic(characteristicIdentifier, valueBase64, response, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - }); - } - - @ReactMethod - public void readCharacteristicForDevice(final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String transactionId, - final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - bleAdapter.readCharacteristicForDevice( - deviceId, serviceUUID, characteristicUUID, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } - - @ReactMethod - public void readCharacteristicForService(final int serviceIdentifier, - final String characteristicUUID, - final String transactionId, - final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - bleAdapter.readCharacteristicForService( - serviceIdentifier, characteristicUUID, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } - - @ReactMethod - public void readCharacteristic(final int characteristicIdentifier, - final String transactionId, - final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - - bleAdapter.readCharacteristic( - characteristicIdentifier, transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Characteristic data) { - safePromise.resolve(characteristicConverter.toJSObject(data)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } - - @ReactMethod - public void monitorCharacteristicForDevice(final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String transactionId, - final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.monitorCharacteristicForDevice( - deviceId, serviceUUID, characteristicUUID, transactionId, - new OnEventCallback() { - @Override - public void onEvent(Characteristic data) { - WritableArray jsResult = Arguments.createArray(); - jsResult.pushNull(); - jsResult.pushMap(characteristicConverter.toJSObject(data)); - jsResult.pushString(transactionId); - sendEvent(Event.ReadEvent, jsResult); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } - - @ReactMethod - public void monitorCharacteristicForService(final int serviceIdentifier, - final String characteristicUUID, - final String transactionId, - final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - bleAdapter.monitorCharacteristicForService( - serviceIdentifier, characteristicUUID, transactionId, - new OnEventCallback() { - @Override - public void onEvent(Characteristic data) { - WritableArray jsResult = Arguments.createArray(); - jsResult.pushNull(); - jsResult.pushMap(characteristicConverter.toJSObject(data)); - jsResult.pushString(transactionId); - sendEvent(Event.ReadEvent, jsResult); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } + final SafePromise safePromise = new SafePromise(promise); + //TODO resolve safePromise with null when monitoring has been completed + bleAdapter.monitorCharacteristic( + characteristicIdentifier, transactionId, + new OnEventCallback() { + @Override + public void onEvent(Characteristic data) { + WritableArray jsResult = Arguments.createArray(); + jsResult.pushNull(); + jsResult.pushMap(characteristicConverter.toJSObject(data)); + jsResult.pushString(transactionId); + sendEvent(Event.ReadEvent, jsResult); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { + safePromise.reject(null, errorConverter.toJs(error)); + } + } + ); + } - @ReactMethod - public void monitorCharacteristic(final int characteristicIdentifier, + @ReactMethod + public void readDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, final String transactionId, final Promise promise) { - final SafePromise safePromise = new SafePromise(promise); - //TODO resolve safePromise with null when monitoring has been completed - bleAdapter.monitorCharacteristic( - characteristicIdentifier, transactionId, - new OnEventCallback() { - @Override - public void onEvent(Characteristic data) { - WritableArray jsResult = Arguments.createArray(); - jsResult.pushNull(); - jsResult.pushMap(characteristicConverter.toJSObject(data)); - jsResult.pushString(transactionId); - sendEvent(Event.ReadEvent, jsResult); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError error) { - safePromise.reject(null, errorConverter.toJs(error)); - } - } - ); - } + bleAdapter.readDescriptorForDevice( + deviceId, + serviceUUID, + characteristicUUID, + descriptorUUID, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + }); + } + + @ReactMethod + public void readDescriptorForService(final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + final Promise promise) { + bleAdapter.readDescriptorForService( + serviceIdentifier, + characteristicUUID, + descriptorUUID, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + }); + } + + @ReactMethod + public void readDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String transactionId, + final Promise promise) { + bleAdapter.readDescriptorForCharacteristic( + characteristicIdentifier, + descriptorUUID, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + }); + } + + @ReactMethod + public void readDescriptor(final int descriptorIdentifier, + final String transactionId, + final Promise promise) { + bleAdapter.readDescriptor( + descriptorIdentifier, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + }); + } + + @ReactMethod + public void writeDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + final Promise promise) { + bleAdapter.writeDescriptorForDevice( + deviceId, + serviceUUID, + characteristicUUID, + descriptorUUID, + valueBase64, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + } + ); + } - @ReactMethod - public void readDescriptorForDevice(final String deviceId, - final String serviceUUID, + @ReactMethod + public void writeDescriptorForService(final int serviceIdentifier, final String characteristicUUID, final String descriptorUUID, + final String valueBase64, final String transactionId, final Promise promise) { - bleAdapter.readDescriptorForDevice( - deviceId, - serviceUUID, - characteristicUUID, - descriptorUUID, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - }); - } - - @ReactMethod - public void readDescriptorForService(final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String transactionId, - final Promise promise) { - bleAdapter.readDescriptorForService( - serviceIdentifier, - characteristicUUID, - descriptorUUID, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - }); - } - - @ReactMethod - public void readDescriptorForCharacteristic(final int characteristicIdentifier, - final String descriptorUUID, - final String transactionId, - final Promise promise) { - bleAdapter.readDescriptorForCharacteristic( - characteristicIdentifier, - descriptorUUID, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - }); - } - - @ReactMethod - public void readDescriptor(final int descriptorIdentifier, - final String transactionId, - final Promise promise) { - bleAdapter.readDescriptor( - descriptorIdentifier, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - }); - } - - @ReactMethod - public void writeDescriptorForDevice(final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - final Promise promise) { - bleAdapter.writeDescriptorForDevice( - deviceId, - serviceUUID, - characteristicUUID, - descriptorUUID, - valueBase64, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - } - ); - } - - @ReactMethod - public void writeDescriptorForService(final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - final Promise promise) { - bleAdapter.writeDescriptorForService( - serviceIdentifier, - characteristicUUID, - descriptorUUID, - valueBase64, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - } - ); - } - - @ReactMethod - public void writeDescriptorForCharacteristic(final int characteristicIdentifier, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - final Promise promise) { - bleAdapter.writeDescriptorForCharacteristic( - characteristicIdentifier, - descriptorUUID, - valueBase64, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - } - ); - } - - @ReactMethod - public void writeDescriptor(final int descriptorIdentifier, - final String valueBase64, - final String transactionId, - final Promise promise) { - bleAdapter.writeDescriptor( - descriptorIdentifier, - valueBase64, - transactionId, - new OnSuccessCallback() { - @Override - public void onSuccess(Descriptor descriptor) { - promise.resolve(descriptorConverter.toJSObject(descriptor)); - } - }, - new OnErrorCallback() { - @Override - public void onError(BleError bleError) { - promise.reject(null, errorConverter.toJs(bleError)); - } - } - ); - } - - @ReactMethod - public void addListener(String eventName) { - // Keep: Required for RN built in Event Emitter Calls. - } - - @ReactMethod - public void removeListeners(int count) { - // Keep: Required for RN built in Event Emitter Calls. - } - - private void sendEvent(@NonNull Event event, @Nullable Object params) { - getReactApplicationContext() - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit(event.name, params); - } + bleAdapter.writeDescriptorForService( + serviceIdentifier, + characteristicUUID, + descriptorUUID, + valueBase64, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + } + ); + } + + @ReactMethod + public void writeDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + final Promise promise) { + bleAdapter.writeDescriptorForCharacteristic( + characteristicIdentifier, + descriptorUUID, + valueBase64, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + } + ); + } + + @ReactMethod + public void writeDescriptor(final int descriptorIdentifier, + final String valueBase64, + final String transactionId, + final Promise promise) { + bleAdapter.writeDescriptor( + descriptorIdentifier, + valueBase64, + transactionId, + new OnSuccessCallback() { + @Override + public void onSuccess(Descriptor descriptor) { + promise.resolve(descriptorConverter.toJSObject(descriptor)); + } + }, + new OnErrorCallback() { + @Override + public void onError(BleError bleError) { + promise.reject(null, errorConverter.toJs(bleError)); + } + } + ); + } + + @ReactMethod + public void addListener(String eventName) { + // Keep: Required for RN built in Event Emitter Calls. + } + + @ReactMethod + public void removeListeners(int count) { + // Keep: Required for RN built in Event Emitter Calls. + } + + private void sendEvent(@NonNull Event event, @Nullable Object params) { + getReactApplicationContext() + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit(event.name, params); + } } diff --git a/android/src/main/java/com/bleplx/Event.java b/android/src/main/java/com/bleplx/Event.java index 215fbabe..cadcde88 100644 --- a/android/src/main/java/com/bleplx/Event.java +++ b/android/src/main/java/com/bleplx/Event.java @@ -2,15 +2,15 @@ public enum Event { - ScanEvent("ScanEvent"), - ReadEvent("ReadEvent"), - StateChangeEvent("StateChangeEvent"), - RestoreStateEvent("RestoreStateEvent"), - DisconnectionEvent("DisconnectionEvent"); + ScanEvent("ScanEvent"), + ReadEvent("ReadEvent"), + StateChangeEvent("StateChangeEvent"), + RestoreStateEvent("RestoreStateEvent"), + DisconnectionEvent("DisconnectionEvent"); - public String name; + public String name; - Event(String name) { - this.name = name; - } + Event(String name) { + this.name = name; + } } diff --git a/android/src/main/java/com/bleplx/adapter/AdvertisementData.java b/android/src/main/java/com/bleplx/adapter/AdvertisementData.java index 35fbb3b1..21a6d51f 100644 --- a/android/src/main/java/com/bleplx/adapter/AdvertisementData.java +++ b/android/src/main/java/com/bleplx/adapter/AdvertisementData.java @@ -13,238 +13,239 @@ public class AdvertisementData { - private byte[] manufacturerData; - private Map serviceData; - private List serviceUUIDs; - private String localName; - private Integer txPowerLevel; - private List solicitedServiceUUIDs; - private byte[] rawScanRecord; - - private static final long BLUETOOTH_BASE_UUID_LSB = 0x800000805F9B34FBL; - private static final int BLUETOOTH_BASE_UUID_MSB = 0x00001000; - - public String getLocalName() { - return localName; - } - - public byte[] getManufacturerData() { - return manufacturerData; - } - - public Map getServiceData() { - return serviceData; - } - - public List getServiceUUIDs() { - return serviceUUIDs; - } - - public Integer getTxPowerLevel() { - return txPowerLevel; - } - - public List getSolicitedServiceUUIDs() { - return solicitedServiceUUIDs; - } - - public byte[] getRawScanRecord() { - return rawScanRecord; - } - - private AdvertisementData() {} - - public AdvertisementData(byte[] manufacturerData, - Map serviceData, - List serviceUUIDs, - String localName, - Integer txPowerLevel, - List solicitedServiceUUIDs) { - this.manufacturerData = manufacturerData; - this.serviceData = serviceData; - this.serviceUUIDs = serviceUUIDs; - this.localName = localName; - this.txPowerLevel = txPowerLevel; - this.solicitedServiceUUIDs = solicitedServiceUUIDs; - } - - public static AdvertisementData parseScanResponseData(byte[] advertisement) { - AdvertisementData advData = new AdvertisementData(); - advData.rawScanRecord = advertisement; - - ByteBuffer rawData = ByteBuffer.wrap(advertisement).order(ByteOrder.LITTLE_ENDIAN); - while (rawData.remaining() >= 2) { - int adLength = rawData.get() & 0xFF; - if (adLength == 0) break; - adLength -= 1; - int adType = rawData.get() & 0xFF; - if (rawData.remaining() < adLength) break; - parseAdvertisementData(advData, adType, adLength, rawData.slice().order(ByteOrder.LITTLE_ENDIAN)); - rawData.position(rawData.position() + adLength); - } - return advData; - } - - private static void parseAdvertisementData(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { - switch (adType) { - case 0xFF: - parseManufacturerData(advData, adLength, data); - break; - - case 0x02: - case 0x03: - parseServiceUUIDs(advData, adLength, data, 2); - break; - case 0x04: - case 0x05: - parseServiceUUIDs(advData, adLength, data, 4); - break; - case 0x06: - case 0x07: - parseServiceUUIDs(advData, adLength, data, 16); - break; - - case 0x08: - case 0x09: - parseLocalName(advData, adType, adLength, data); - break; - - case 0x0A: - parseTxPowerLevel(advData, adLength, data); - break; - - case 0x14: - parseSolicitedServiceUUIDs(advData, adLength, data, 2); - break; - case 0x1F: - parseSolicitedServiceUUIDs(advData, adLength, data, 4); - break; - case 0x15: - parseSolicitedServiceUUIDs(advData, adLength, data, 16); - break; - - case 0x16: - parseServiceData(advData, adLength, data, 2); - break; - case 0x20: - parseServiceData(advData, adLength, data, 4); - break; - case 0x21: - parseServiceData(advData, adLength, data, 16); - break; - } - } - - private static void parseLocalName(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { - // Complete local name is preferred over short local name. - if (advData.localName == null || adType == 0x09) { - byte[] bytes = new byte[adLength]; - data.get(bytes, 0, adLength); - advData.localName = new String(bytes, Charset.forName("UTF-8")); - } - } - - private static UUID parseUUID(ByteBuffer data, int uuidLength) { - long lsb; - long msb; - switch (uuidLength) { - case 2: - msb = (((long) data.getShort() & 0xFFFF) << 32) + BLUETOOTH_BASE_UUID_MSB; - lsb = BLUETOOTH_BASE_UUID_LSB; - break; - case 4: - msb = ((long) data.getInt() << 32) + BLUETOOTH_BASE_UUID_MSB; - lsb = BLUETOOTH_BASE_UUID_LSB; - break; - case 16: - lsb = data.getLong(); - msb = data.getLong(); - break; - default: - data.position(data.position() + uuidLength); - return null; - } - return new UUID(msb, lsb); - } - - private static void parseSolicitedServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { - if (advData.solicitedServiceUUIDs == null) advData.solicitedServiceUUIDs = new ArrayList<>(); - while (data.remaining() >= uuidLength && data.position() < adLength) { - advData.solicitedServiceUUIDs.add(parseUUID(data, uuidLength)); - } - } - - private static void parseServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { - if (advData.serviceUUIDs == null) advData.serviceUUIDs = new ArrayList<>(); - while (data.remaining() >= uuidLength && data.position() < adLength) { - advData.serviceUUIDs.add(parseUUID(data, uuidLength)); - } - } - - private static void parseServiceData(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { - if (adLength < uuidLength) return; - if (advData.serviceData == null) advData.serviceData = new HashMap<>(); - UUID serviceUUID = parseUUID(data, uuidLength); - int serviceDataLength = adLength - uuidLength; - byte[] serviceData = new byte[serviceDataLength]; - data.get(serviceData, 0, serviceDataLength); - advData.serviceData.put(serviceUUID, serviceData); - } - - private static void parseTxPowerLevel(AdvertisementData advData, int adLength, ByteBuffer data) { - if (adLength != 1) return; - advData.txPowerLevel = (int) data.get(); - } - - private static void parseManufacturerData(AdvertisementData advData, int adLength, ByteBuffer data) { - if (adLength < 2) return; - advData.manufacturerData = new byte[adLength]; - data.get(advData.manufacturerData, 0, adLength); - } - - @Override - public String toString() { - return "AdvertisementData{" + - "manufacturerData=" + Arrays.toString(manufacturerData) + - ", serviceData=" + serviceData + - ", serviceUUIDs=" + serviceUUIDs + - ", localName='" + localName + '\'' + - ", txPowerLevel=" + txPowerLevel + - ", solicitedServiceUUIDs=" + solicitedServiceUUIDs + - ", rawScanRecord=" + Arrays.toString(rawScanRecord) + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - AdvertisementData that = (AdvertisementData) o; - - if (!Arrays.equals(manufacturerData, that.manufacturerData)) return false; - if (!Objects.equals(serviceData, that.serviceData)) - return false; - if (!Objects.equals(serviceUUIDs, that.serviceUUIDs)) - return false; - if (!Objects.equals(localName, that.localName)) - return false; - if (!Objects.equals(txPowerLevel, that.txPowerLevel)) - return false; - if (!Objects.equals(solicitedServiceUUIDs, that.solicitedServiceUUIDs)) - return false; - return Arrays.equals(rawScanRecord, that.rawScanRecord); - } - - @Override - public int hashCode() { - int result = Arrays.hashCode(manufacturerData); - result = 31 * result + (serviceData != null ? serviceData.hashCode() : 0); - result = 31 * result + (serviceUUIDs != null ? serviceUUIDs.hashCode() : 0); - result = 31 * result + (localName != null ? localName.hashCode() : 0); - result = 31 * result + (txPowerLevel != null ? txPowerLevel.hashCode() : 0); - result = 31 * result + (solicitedServiceUUIDs != null ? solicitedServiceUUIDs.hashCode() : 0); - result = 31 * result + Arrays.hashCode(rawScanRecord); - return result; - } + private byte[] manufacturerData; + private Map serviceData; + private List serviceUUIDs; + private String localName; + private Integer txPowerLevel; + private List solicitedServiceUUIDs; + private byte[] rawScanRecord; + + private static final long BLUETOOTH_BASE_UUID_LSB = 0x800000805F9B34FBL; + private static final int BLUETOOTH_BASE_UUID_MSB = 0x00001000; + + public String getLocalName() { + return localName; + } + + public byte[] getManufacturerData() { + return manufacturerData; + } + + public Map getServiceData() { + return serviceData; + } + + public List getServiceUUIDs() { + return serviceUUIDs; + } + + public Integer getTxPowerLevel() { + return txPowerLevel; + } + + public List getSolicitedServiceUUIDs() { + return solicitedServiceUUIDs; + } + + public byte[] getRawScanRecord() { + return rawScanRecord; + } + + private AdvertisementData() { + } + + public AdvertisementData(byte[] manufacturerData, + Map serviceData, + List serviceUUIDs, + String localName, + Integer txPowerLevel, + List solicitedServiceUUIDs) { + this.manufacturerData = manufacturerData; + this.serviceData = serviceData; + this.serviceUUIDs = serviceUUIDs; + this.localName = localName; + this.txPowerLevel = txPowerLevel; + this.solicitedServiceUUIDs = solicitedServiceUUIDs; + } + + public static AdvertisementData parseScanResponseData(byte[] advertisement) { + AdvertisementData advData = new AdvertisementData(); + advData.rawScanRecord = advertisement; + + ByteBuffer rawData = ByteBuffer.wrap(advertisement).order(ByteOrder.LITTLE_ENDIAN); + while (rawData.remaining() >= 2) { + int adLength = rawData.get() & 0xFF; + if (adLength == 0) break; + adLength -= 1; + int adType = rawData.get() & 0xFF; + if (rawData.remaining() < adLength) break; + parseAdvertisementData(advData, adType, adLength, rawData.slice().order(ByteOrder.LITTLE_ENDIAN)); + rawData.position(rawData.position() + adLength); + } + return advData; + } + + private static void parseAdvertisementData(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { + switch (adType) { + case 0xFF: + parseManufacturerData(advData, adLength, data); + break; + + case 0x02: + case 0x03: + parseServiceUUIDs(advData, adLength, data, 2); + break; + case 0x04: + case 0x05: + parseServiceUUIDs(advData, adLength, data, 4); + break; + case 0x06: + case 0x07: + parseServiceUUIDs(advData, adLength, data, 16); + break; + + case 0x08: + case 0x09: + parseLocalName(advData, adType, adLength, data); + break; + + case 0x0A: + parseTxPowerLevel(advData, adLength, data); + break; + + case 0x14: + parseSolicitedServiceUUIDs(advData, adLength, data, 2); + break; + case 0x1F: + parseSolicitedServiceUUIDs(advData, adLength, data, 4); + break; + case 0x15: + parseSolicitedServiceUUIDs(advData, adLength, data, 16); + break; + + case 0x16: + parseServiceData(advData, adLength, data, 2); + break; + case 0x20: + parseServiceData(advData, adLength, data, 4); + break; + case 0x21: + parseServiceData(advData, adLength, data, 16); + break; + } + } + + private static void parseLocalName(AdvertisementData advData, int adType, int adLength, ByteBuffer data) { + // Complete local name is preferred over short local name. + if (advData.localName == null || adType == 0x09) { + byte[] bytes = new byte[adLength]; + data.get(bytes, 0, adLength); + advData.localName = new String(bytes, Charset.forName("UTF-8")); + } + } + + private static UUID parseUUID(ByteBuffer data, int uuidLength) { + long lsb; + long msb; + switch (uuidLength) { + case 2: + msb = (((long) data.getShort() & 0xFFFF) << 32) + BLUETOOTH_BASE_UUID_MSB; + lsb = BLUETOOTH_BASE_UUID_LSB; + break; + case 4: + msb = ((long) data.getInt() << 32) + BLUETOOTH_BASE_UUID_MSB; + lsb = BLUETOOTH_BASE_UUID_LSB; + break; + case 16: + lsb = data.getLong(); + msb = data.getLong(); + break; + default: + data.position(data.position() + uuidLength); + return null; + } + return new UUID(msb, lsb); + } + + private static void parseSolicitedServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (advData.solicitedServiceUUIDs == null) advData.solicitedServiceUUIDs = new ArrayList<>(); + while (data.remaining() >= uuidLength && data.position() < adLength) { + advData.solicitedServiceUUIDs.add(parseUUID(data, uuidLength)); + } + } + + private static void parseServiceUUIDs(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (advData.serviceUUIDs == null) advData.serviceUUIDs = new ArrayList<>(); + while (data.remaining() >= uuidLength && data.position() < adLength) { + advData.serviceUUIDs.add(parseUUID(data, uuidLength)); + } + } + + private static void parseServiceData(AdvertisementData advData, int adLength, ByteBuffer data, int uuidLength) { + if (adLength < uuidLength) return; + if (advData.serviceData == null) advData.serviceData = new HashMap<>(); + UUID serviceUUID = parseUUID(data, uuidLength); + int serviceDataLength = adLength - uuidLength; + byte[] serviceData = new byte[serviceDataLength]; + data.get(serviceData, 0, serviceDataLength); + advData.serviceData.put(serviceUUID, serviceData); + } + + private static void parseTxPowerLevel(AdvertisementData advData, int adLength, ByteBuffer data) { + if (adLength != 1) return; + advData.txPowerLevel = (int) data.get(); + } + + private static void parseManufacturerData(AdvertisementData advData, int adLength, ByteBuffer data) { + if (adLength < 2) return; + advData.manufacturerData = new byte[adLength]; + data.get(advData.manufacturerData, 0, adLength); + } + + @Override + public String toString() { + return "AdvertisementData{" + + "manufacturerData=" + Arrays.toString(manufacturerData) + + ", serviceData=" + serviceData + + ", serviceUUIDs=" + serviceUUIDs + + ", localName='" + localName + '\'' + + ", txPowerLevel=" + txPowerLevel + + ", solicitedServiceUUIDs=" + solicitedServiceUUIDs + + ", rawScanRecord=" + Arrays.toString(rawScanRecord) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AdvertisementData that = (AdvertisementData) o; + + if (!Arrays.equals(manufacturerData, that.manufacturerData)) return false; + if (!Objects.equals(serviceData, that.serviceData)) + return false; + if (!Objects.equals(serviceUUIDs, that.serviceUUIDs)) + return false; + if (!Objects.equals(localName, that.localName)) + return false; + if (!Objects.equals(txPowerLevel, that.txPowerLevel)) + return false; + if (!Objects.equals(solicitedServiceUUIDs, that.solicitedServiceUUIDs)) + return false; + return Arrays.equals(rawScanRecord, that.rawScanRecord); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(manufacturerData); + result = 31 * result + (serviceData != null ? serviceData.hashCode() : 0); + result = 31 * result + (serviceUUIDs != null ? serviceUUIDs.hashCode() : 0); + result = 31 * result + (localName != null ? localName.hashCode() : 0); + result = 31 * result + (txPowerLevel != null ? txPowerLevel.hashCode() : 0); + result = 31 * result + (solicitedServiceUUIDs != null ? solicitedServiceUUIDs.hashCode() : 0); + result = 31 * result + Arrays.hashCode(rawScanRecord); + return result; + } } diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapter.java b/android/src/main/java/com/bleplx/adapter/BleAdapter.java index e0502ae7..6c26d45c 100644 --- a/android/src/main/java/com/bleplx/adapter/BleAdapter.java +++ b/android/src/main/java/com/bleplx/adapter/BleAdapter.java @@ -6,246 +6,246 @@ public interface BleAdapter { - void createClient(String restoreStateIdentifier, - OnEventCallback onAdapterStateChangeCallback, - OnEventCallback onStateRestored); - - void destroyClient(); - - void enable( - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void disable( - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - String getCurrentState(); - - void startDeviceScan( - String[] filteredUUIDs, - int scanMode, - int callbackType, - boolean legacyScan, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback); - - void stopDeviceScan(); - - void requestConnectionPriorityForDevice( - String deviceIdentifier, - int connectionPriority, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void readRSSIForDevice( - String deviceIdentifier, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void requestMTUForDevice( - String deviceIdentifier, - int mtu, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void getKnownDevices( - String[] deviceIdentifiers, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void getConnectedDevices( - String[] serviceUUIDs, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void connectToDevice( - String deviceIdentifier, - ConnectionOptions connectionOptions, - OnSuccessCallback onSuccessCallback, - OnEventCallback onConnectionStateChangedCallback, - OnErrorCallback onErrorCallback); - - void cancelDeviceConnection( - String deviceIdentifier, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void isDeviceConnected( - String deviceIdentifier, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void discoverAllServicesAndCharacteristicsForDevice( - String deviceIdentifier, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - List getServicesForDevice( - String deviceIdentifier) throws BleError; - - List getCharacteristicsForDevice( - String deviceIdentifier, - String serviceUUID) throws BleError; - - List getCharacteristicsForService( - int serviceIdentifier) throws BleError; - - List descriptorsForDevice( - String deviceIdentifier, - String serviceUUID, - String characteristicUUID) throws BleError; - - List descriptorsForService( - int serviceIdentifier, - String characteristicUUID) throws BleError; - - List descriptorsForCharacteristic( - int characteristicIdentifier) throws BleError; - - - void readCharacteristicForDevice( - String deviceIdentifier, - String serviceUUID, - String characteristicUUID, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void readCharacteristicForService( - int serviceIdentifier, - String characteristicUUID, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void readCharacteristic( - int characteristicIdentifer, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void writeCharacteristicForDevice( - String deviceIdentifier, - String serviceUUID, - String characteristicUUID, - String valueBase64, - boolean withResponse, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void writeCharacteristicForService( - int serviceIdentifier, - String characteristicUUID, - String valueBase64, - boolean withResponse, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void writeCharacteristic( - int characteristicIdentifier, - String valueBase64, - boolean withResponse, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void monitorCharacteristicForDevice( - String deviceIdentifier, - String serviceUUID, - String characteristicUUID, - String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback); - - void monitorCharacteristicForService( - int serviceIdentifier, - String characteristicUUID, - String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback); - - void monitorCharacteristic( - int characteristicIdentifier, - String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback); - - void readDescriptorForDevice( - final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String descriptorUUID, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void readDescriptorForService( - final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void readDescriptorForCharacteristic( - final int characteristicIdentifier, - final String descriptorUUID, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void readDescriptor( - final int descriptorIdentifier, - final String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback); - - void writeDescriptorForDevice( - final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void writeDescriptorForService( - final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void writeDescriptorForCharacteristic( - final int characteristicIdentifier, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void writeDescriptor( - final int descriptorIdentifier, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback); - - void cancelTransaction(String transactionId); - - void setLogLevel(String logLevel); - - String getLogLevel(); + void createClient(String restoreStateIdentifier, + OnEventCallback onAdapterStateChangeCallback, + OnEventCallback onStateRestored); + + void destroyClient(); + + void enable( + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void disable( + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + String getCurrentState(); + + void startDeviceScan( + String[] filteredUUIDs, + int scanMode, + int callbackType, + boolean legacyScan, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void stopDeviceScan(); + + void requestConnectionPriorityForDevice( + String deviceIdentifier, + int connectionPriority, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readRSSIForDevice( + String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void requestMTUForDevice( + String deviceIdentifier, + int mtu, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void getKnownDevices( + String[] deviceIdentifiers, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void getConnectedDevices( + String[] serviceUUIDs, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void connectToDevice( + String deviceIdentifier, + ConnectionOptions connectionOptions, + OnSuccessCallback onSuccessCallback, + OnEventCallback onConnectionStateChangedCallback, + OnErrorCallback onErrorCallback); + + void cancelDeviceConnection( + String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void isDeviceConnected( + String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void discoverAllServicesAndCharacteristicsForDevice( + String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + List getServicesForDevice( + String deviceIdentifier) throws BleError; + + List getCharacteristicsForDevice( + String deviceIdentifier, + String serviceUUID) throws BleError; + + List getCharacteristicsForService( + int serviceIdentifier) throws BleError; + + List descriptorsForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID) throws BleError; + + List descriptorsForService( + int serviceIdentifier, + String characteristicUUID) throws BleError; + + List descriptorsForCharacteristic( + int characteristicIdentifier) throws BleError; + + + void readCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void readCharacteristic( + int characteristicIdentifer, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeCharacteristic( + int characteristicIdentifier, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristicForDevice( + String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristicForService( + int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void monitorCharacteristic( + int characteristicIdentifier, + String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback); + + void readDescriptorForDevice( + final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptorForService( + final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptorForCharacteristic( + final int characteristicIdentifier, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void readDescriptor( + final int descriptorIdentifier, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback); + + void writeDescriptorForDevice( + final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptorForService( + final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptorForCharacteristic( + final int characteristicIdentifier, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void writeDescriptor( + final int descriptorIdentifier, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback); + + void cancelTransaction(String transactionId); + + void setLogLevel(String logLevel); + + String getLogLevel(); } diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java b/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java index 1d9fb361..93223f14 100644 --- a/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java +++ b/android/src/main/java/com/bleplx/adapter/BleAdapterCreator.java @@ -3,5 +3,5 @@ import android.content.Context; public interface BleAdapterCreator { - BleAdapter createAdapter(Context context); + BleAdapter createAdapter(Context context); } diff --git a/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java b/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java index b2143895..57fc3a63 100644 --- a/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java +++ b/android/src/main/java/com/bleplx/adapter/BleAdapterFactory.java @@ -4,18 +4,18 @@ public class BleAdapterFactory { - private static BleAdapterCreator bleAdapterCreator = new BleAdapterCreator() { - @Override - public BleAdapter createAdapter(Context context) { - return new BleModule(context); - } - }; - - public static BleAdapter getNewAdapter(Context context) { - return bleAdapterCreator.createAdapter(context); + private static BleAdapterCreator bleAdapterCreator = new BleAdapterCreator() { + @Override + public BleAdapter createAdapter(Context context) { + return new BleModule(context); } + }; - public static void setBleAdapterCreator(BleAdapterCreator bleAdapterCreator) { - BleAdapterFactory.bleAdapterCreator = bleAdapterCreator; - } + public static BleAdapter getNewAdapter(Context context) { + return bleAdapterCreator.createAdapter(context); + } + + public static void setBleAdapterCreator(BleAdapterCreator bleAdapterCreator) { + BleAdapterFactory.bleAdapterCreator = bleAdapterCreator; + } } diff --git a/android/src/main/java/com/bleplx/adapter/BleModule.java b/android/src/main/java/com/bleplx/adapter/BleModule.java index 1c46cb51..67c2bd87 100755 --- a/android/src/main/java/com/bleplx/adapter/BleModule.java +++ b/android/src/main/java/com/bleplx/adapter/BleModule.java @@ -58,1512 +58,1512 @@ public class BleModule implements BleAdapter { - private final ErrorConverter errorConverter = new ErrorConverter(); + private final ErrorConverter errorConverter = new ErrorConverter(); - @Nullable - private RxBleClient rxBleClient; + @Nullable + private RxBleClient rxBleClient; - private final HashMap discoveredDevices = new HashMap<>(); + private final HashMap discoveredDevices = new HashMap<>(); - private final HashMap connectedDevices = new HashMap<>(); + private final HashMap connectedDevices = new HashMap<>(); - private final HashMap activeConnections = new HashMap<>(); + private final HashMap activeConnections = new HashMap<>(); - private final SparseArray discoveredServices = new SparseArray<>(); + private final SparseArray discoveredServices = new SparseArray<>(); - private final SparseArray discoveredCharacteristics = new SparseArray<>(); + private final SparseArray discoveredCharacteristics = new SparseArray<>(); - private final SparseArray discoveredDescriptors = new SparseArray<>(); + private final SparseArray discoveredDescriptors = new SparseArray<>(); - private final DisposableMap pendingTransactions = new DisposableMap(); + private final DisposableMap pendingTransactions = new DisposableMap(); - private final DisposableMap connectingDevices = new DisposableMap(); + private final DisposableMap connectingDevices = new DisposableMap(); - private final BluetoothManager bluetoothManager; + private final BluetoothManager bluetoothManager; - private final BluetoothAdapter bluetoothAdapter; + private final BluetoothAdapter bluetoothAdapter; - private final Context context; + private final Context context; - @Nullable - private Disposable scanSubscription; + @Nullable + private Disposable scanSubscription; - @Nullable - private Disposable adapterStateChangesSubscription; + @Nullable + private Disposable adapterStateChangesSubscription; - private final RxBleDeviceToDeviceMapper rxBleDeviceToDeviceMapper = new RxBleDeviceToDeviceMapper(); + private final RxBleDeviceToDeviceMapper rxBleDeviceToDeviceMapper = new RxBleDeviceToDeviceMapper(); - private final RxScanResultToScanResultMapper rxScanResultToScanResultMapper = new RxScanResultToScanResultMapper(); + private final RxScanResultToScanResultMapper rxScanResultToScanResultMapper = new RxScanResultToScanResultMapper(); - private final ServiceFactory serviceFactory = new ServiceFactory(); + private final ServiceFactory serviceFactory = new ServiceFactory(); - private int currentLogLevel = RxBleLog.NONE; + private int currentLogLevel = RxBleLog.NONE; - public BleModule(Context context) { - this.context = context; - bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); - bluetoothAdapter = bluetoothManager.getAdapter(); - } + public BleModule(Context context) { + this.context = context; + bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + bluetoothAdapter = bluetoothManager.getAdapter(); + } - @Override - public void createClient(String restoreStateIdentifier, - OnEventCallback onAdapterStateChangeCallback, - OnEventCallback onStateRestored) { - rxBleClient = RxBleClient.create(context); - adapterStateChangesSubscription = monitorAdapterStateChanges(context, onAdapterStateChangeCallback); + @Override + public void createClient(String restoreStateIdentifier, + OnEventCallback onAdapterStateChangeCallback, + OnEventCallback onStateRestored) { + rxBleClient = RxBleClient.create(context); + adapterStateChangesSubscription = monitorAdapterStateChanges(context, onAdapterStateChangeCallback); - // We need to send signal that BLE Module starts without restored state - if (restoreStateIdentifier != null) { - onStateRestored.onEvent(null); - } + // We need to send signal that BLE Module starts without restored state + if (restoreStateIdentifier != null) { + onStateRestored.onEvent(null); } + } - @Override - public void destroyClient() { - if (adapterStateChangesSubscription != null) { - adapterStateChangesSubscription.dispose(); - adapterStateChangesSubscription = null; - } - if (scanSubscription != null && !scanSubscription.isDisposed()) { - scanSubscription.dispose(); - scanSubscription = null; - } - pendingTransactions.removeAllSubscriptions(); - connectingDevices.removeAllSubscriptions(); - - discoveredServices.clear(); - discoveredCharacteristics.clear(); - discoveredDescriptors.clear(); - connectedDevices.clear(); - activeConnections.clear(); - discoveredDevices.clear(); - - rxBleClient = null; - IdGenerator.clear(); - } - - - @Override - public void enable(final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - changeAdapterState( - RxBleAdapterStateObservable.BleAdapterState.STATE_ON, - transactionId, - onSuccessCallback, - onErrorCallback); - } - - @Override - public void disable(final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - changeAdapterState( - RxBleAdapterStateObservable.BleAdapterState.STATE_OFF, - transactionId, - onSuccessCallback, - onErrorCallback); - } - - @BluetoothState - @Override - public String getCurrentState() { - if (!supportsBluetoothLowEnergy()) return BluetoothState.UNSUPPORTED; - if (bluetoothManager == null) return BluetoothState.POWERED_OFF; - return mapNativeAdapterStateToLocalBluetoothState(bluetoothAdapter.getState()); - } - - @Override - public void startDeviceScan(String[] filteredUUIDs, - int scanMode, - int callbackType, - boolean legacyScan, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback) { - UUID[] uuids = null; + @Override + public void destroyClient() { + if (adapterStateChangesSubscription != null) { + adapterStateChangesSubscription.dispose(); + adapterStateChangesSubscription = null; + } + if (scanSubscription != null && !scanSubscription.isDisposed()) { + scanSubscription.dispose(); + scanSubscription = null; + } + pendingTransactions.removeAllSubscriptions(); + connectingDevices.removeAllSubscriptions(); - if (filteredUUIDs != null) { - uuids = UUIDConverter.convert(filteredUUIDs); - if (uuids == null) { - onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(filteredUUIDs)); - return; - } - } + discoveredServices.clear(); + discoveredCharacteristics.clear(); + discoveredDescriptors.clear(); + connectedDevices.clear(); + activeConnections.clear(); + discoveredDevices.clear(); - safeStartDeviceScan(uuids, scanMode, callbackType, legacyScan, onEventCallback, onErrorCallback); + rxBleClient = null; + IdGenerator.clear(); + } + + + @Override + public void enable(final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + changeAdapterState( + RxBleAdapterStateObservable.BleAdapterState.STATE_ON, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void disable(final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + changeAdapterState( + RxBleAdapterStateObservable.BleAdapterState.STATE_OFF, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @BluetoothState + @Override + public String getCurrentState() { + if (!supportsBluetoothLowEnergy()) return BluetoothState.UNSUPPORTED; + if (bluetoothManager == null) return BluetoothState.POWERED_OFF; + return mapNativeAdapterStateToLocalBluetoothState(bluetoothAdapter.getState()); + } + + @Override + public void startDeviceScan(String[] filteredUUIDs, + int scanMode, + int callbackType, + boolean legacyScan, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + UUID[] uuids = null; + + if (filteredUUIDs != null) { + uuids = UUIDConverter.convert(filteredUUIDs); + if (uuids == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(filteredUUIDs)); + return; + } + } + + safeStartDeviceScan(uuids, scanMode, callbackType, legacyScan, onEventCallback, onErrorCallback); + } + + @Override + public void stopDeviceScan() { + if (scanSubscription != null) { + scanSubscription.dispose(); + scanSubscription = null; + } + } + + @Override + public void requestConnectionPriorityForDevice(String deviceIdentifier, + int connectionPriority, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; } - @Override - public void stopDeviceScan() { - if (scanSubscription != null) { - scanSubscription.dispose(); - scanSubscription = null; - } + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; } - @Override - public void requestConnectionPriorityForDevice(String deviceIdentifier, - int connectionPriority, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - final Device device; - try { - device = getDeviceById(deviceIdentifier); - } catch (BleError error) { - onErrorCallback.onError(error); - return; - } + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); - if (connection == null) { - return; - } + final Disposable subscription = connection + .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }).subscribe((Action) () -> { + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + pendingTransactions.replaceSubscription(transactionId, subscription); + } - final Disposable subscription = connection - .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }).subscribe((Action) () -> { - safeExecutor.success(device); - pendingTransactions.removeSubscription(transactionId); - }, throwable -> { - safeExecutor.error(errorConverter.toError(throwable)); - pendingTransactions.removeSubscription(transactionId); - }); + @Override + public void readRSSIForDevice(String deviceIdentifier, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .readRssi() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(rssi -> { + device.setRssi(rssi); + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); - pendingTransactions.replaceSubscription(transactionId, subscription); - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - @Override - public void readRSSIForDevice(String deviceIdentifier, + @Override + public void requestMTUForDevice(String deviceIdentifier, int mtu, final String transactionId, final OnSuccessCallback onSuccessCallback, final OnErrorCallback onErrorCallback) { - final Device device; - try { - device = getDeviceById(deviceIdentifier); - } catch (BleError error) { - onErrorCallback.onError(error); - return; - } - final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .readRssi() - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(rssi -> { - device.setRssi(rssi); - safeExecutor.success(device); - pendingTransactions.removeSubscription(transactionId); - }, error -> { - safeExecutor.error(errorConverter.toError(error)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - @Override - public void requestMTUForDevice(String deviceIdentifier, int mtu, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - final Device device; - try { - device = getDeviceById(deviceIdentifier); - } catch (BleError error) { - onErrorCallback.onError(error); - return; - } - - final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .requestMtu(mtu) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }).subscribe(outputMtu -> { - device.setMtu(outputMtu); - safeExecutor.success(device); - pendingTransactions.removeSubscription(transactionId); - }, error -> { - safeExecutor.error(errorConverter.toError(error)); - pendingTransactions.removeSubscription(transactionId); - }); + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; + } - pendingTransactions.replaceSubscription(transactionId, subscription); + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; } - @Override - public void getKnownDevices(String[] deviceIdentifiers, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get known devices", null)); - return; - } + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - List knownDevices = new ArrayList<>(); - for (final String deviceId : deviceIdentifiers) { - if (deviceId == null) { - onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(deviceIdentifiers)); - return; - } + final Disposable subscription = connection + .requestMtu(mtu) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }).subscribe(outputMtu -> { + device.setMtu(outputMtu); + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); - final Device device = discoveredDevices.get(deviceId); - if (device != null) { - knownDevices.add(device); - } - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - onSuccessCallback.onSuccess(knownDevices.toArray(new Device[knownDevices.size()])); + @Override + public void getKnownDevices(String[] deviceIdentifiers, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get known devices", null)); + return; } - @Override - public void getConnectedDevices(String[] serviceUUIDs, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get connected devices", null)); - return; - } + List knownDevices = new ArrayList<>(); + for (final String deviceId : deviceIdentifiers) { + if (deviceId == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(deviceIdentifiers)); + return; + } - if (serviceUUIDs.length == 0) { - onSuccessCallback.onSuccess(new Device[0]); - return; - } + final Device device = discoveredDevices.get(deviceId); + if (device != null) { + knownDevices.add(device); + } + } - UUID[] uuids = new UUID[serviceUUIDs.length]; - for (int i = 0; i < serviceUUIDs.length; i++) { - UUID uuid = UUIDConverter.convert(serviceUUIDs[i]); + onSuccessCallback.onSuccess(knownDevices.toArray(new Device[knownDevices.size()])); + } - if (uuid == null) { - onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUIDs)); - return; - } + @Override + public void getConnectedDevices(String[] serviceUUIDs, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to get connected devices", null)); + return; + } - uuids[i] = uuid; - } + if (serviceUUIDs.length == 0) { + onSuccessCallback.onSuccess(new Device[0]); + return; + } - List localConnectedDevices = new ArrayList<>(); - for (Device device : connectedDevices.values()) { - for (UUID uuid : uuids) { - if (device.getServiceByUUID(uuid) != null) { - localConnectedDevices.add(device); - break; - } - } - } + UUID[] uuids = new UUID[serviceUUIDs.length]; + for (int i = 0; i < serviceUUIDs.length; i++) { + UUID uuid = UUIDConverter.convert(serviceUUIDs[i]); - onSuccessCallback.onSuccess(localConnectedDevices.toArray(new Device[localConnectedDevices.size()])); + if (uuid == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUIDs)); + return; + } + uuids[i] = uuid; } - @Override - public void connectToDevice(String deviceIdentifier, - ConnectionOptions connectionOptions, - OnSuccessCallback onSuccessCallback, - OnEventCallback onConnectionStateChangedCallback, - OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to connect to device", null)); - return; - } - - final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); - if (device == null) { - onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); - return; + List localConnectedDevices = new ArrayList<>(); + for (Device device : connectedDevices.values()) { + for (UUID uuid : uuids) { + if (device.getServiceByUUID(uuid) != null) { + localConnectedDevices.add(device); + break; } + } + } - safeConnectToDevice( - device, - connectionOptions.getAutoConnect(), - connectionOptions.getRequestMTU(), - connectionOptions.getRefreshGattMoment(), - connectionOptions.getTimeoutInMillis(), - connectionOptions.getConnectionPriority(), - onSuccessCallback, onConnectionStateChangedCallback, onErrorCallback); - } - - @Override - public void cancelDeviceConnection(String deviceIdentifier, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to cancel device connection", null)); - return; - } + onSuccessCallback.onSuccess(localConnectedDevices.toArray(new Device[localConnectedDevices.size()])); - final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + } - if (connectingDevices.removeSubscription(deviceIdentifier) && device != null) { - onSuccessCallback.onSuccess(rxBleDeviceToDeviceMapper.map(device, null)); - } else { - if (device == null) { - onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); - } else { - onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceIdentifier)); - } - } + @Override + public void connectToDevice(String deviceIdentifier, + ConnectionOptions connectionOptions, + OnSuccessCallback onSuccessCallback, + OnEventCallback onConnectionStateChangedCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to connect to device", null)); + return; } - @Override - public void isDeviceConnected(String deviceIdentifier, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to check if device is connected", null)); - return; - } + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + return; + } - try { - final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); - if (device == null) { - onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); - return; - } + safeConnectToDevice( + device, + connectionOptions.getAutoConnect(), + connectionOptions.getRequestMTU(), + connectionOptions.getRefreshGattMoment(), + connectionOptions.getTimeoutInMillis(), + connectionOptions.getConnectionPriority(), + onSuccessCallback, onConnectionStateChangedCallback, onErrorCallback); + } - boolean connected = device.getConnectionState() - .equals(RxBleConnection.RxBleConnectionState.CONNECTED); - onSuccessCallback.onSuccess(connected); - } catch (Exception e) { - RxBleLog.e(e, "Error while checking if device is connected"); - onErrorCallback.onError(errorConverter.toError(e)); - } + @Override + public void cancelDeviceConnection(String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to cancel device connection", null)); + return; } - @Override - public void discoverAllServicesAndCharacteristicsForDevice(String deviceIdentifier, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final Device device; - try { - device = getDeviceById(deviceIdentifier); - } catch (BleError error) { - onErrorCallback.onError(error); - return; - } + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); - safeDiscoverAllServicesAndCharacteristicsForDevice(device, transactionId, onSuccessCallback, onErrorCallback); + if (connectingDevices.removeSubscription(deviceIdentifier) && device != null) { + onSuccessCallback.onSuccess(rxBleDeviceToDeviceMapper.map(device, null)); + } else { + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + } else { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceIdentifier)); + } } + } - @Override - public List getServicesForDevice(String deviceIdentifier) throws BleError { - final Device device = getDeviceById(deviceIdentifier); - final List services = device.getServices(); - if (services == null) { - throw BleErrorUtils.deviceServicesNotDiscovered(device.getId()); - } - return services; + @Override + public void isDeviceConnected(String deviceIdentifier, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to check if device is connected", null)); + return; } - @Override - public List getCharacteristicsForDevice(String deviceIdentifier, - String serviceUUID) throws BleError { - final UUID convertedServiceUUID = UUIDConverter.convert(serviceUUID); - if (convertedServiceUUID == null) { - throw BleErrorUtils.invalidIdentifiers(serviceUUID); - } + try { + final RxBleDevice device = rxBleClient.getBleDevice(deviceIdentifier); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotFound(deviceIdentifier)); + return; + } - final Device device = getDeviceById(deviceIdentifier); - - final Service service = device.getServiceByUUID(convertedServiceUUID); - if (service == null) { - throw BleErrorUtils.serviceNotFound(serviceUUID); - } - - return service.getCharacteristics(); + boolean connected = device.getConnectionState() + .equals(RxBleConnection.RxBleConnectionState.CONNECTED); + onSuccessCallback.onSuccess(connected); + } catch (Exception e) { + RxBleLog.e(e, "Error while checking if device is connected"); + onErrorCallback.onError(errorConverter.toError(e)); } + } - @Override - public List getCharacteristicsForService(int serviceIdentifier) throws BleError { - Service service = discoveredServices.get(serviceIdentifier); - if (service == null) { - throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); - } - return service.getCharacteristics(); + @Override + public void discoverAllServicesAndCharacteristicsForDevice(String deviceIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Device device; + try { + device = getDeviceById(deviceIdentifier); + } catch (BleError error) { + onErrorCallback.onError(error); + return; } - @Override - public List descriptorsForDevice(final String deviceIdentifier, - final String serviceUUID, - final String characteristicUUID) throws BleError { - final UUID[] uuids = UUIDConverter.convert(serviceUUID, characteristicUUID); - if (uuids == null) { - throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID); - } + safeDiscoverAllServicesAndCharacteristicsForDevice(device, transactionId, onSuccessCallback, onErrorCallback); + } - Device device = getDeviceById(deviceIdentifier); + @Override + public List getServicesForDevice(String deviceIdentifier) throws BleError { + final Device device = getDeviceById(deviceIdentifier); + final List services = device.getServices(); + if (services == null) { + throw BleErrorUtils.deviceServicesNotDiscovered(device.getId()); + } + return services; + } - final Service service = device.getServiceByUUID(uuids[0]); - if (service == null) { - throw BleErrorUtils.serviceNotFound(serviceUUID); - } + @Override + public List getCharacteristicsForDevice(String deviceIdentifier, + String serviceUUID) throws BleError { + final UUID convertedServiceUUID = UUIDConverter.convert(serviceUUID); + if (convertedServiceUUID == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID); + } - final Characteristic characteristic = service.getCharacteristicByUUID(uuids[1]); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(characteristicUUID); - } + final Device device = getDeviceById(deviceIdentifier); - return characteristic.getDescriptors(); + final Service service = device.getServiceByUUID(convertedServiceUUID); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); } - @Override - public List descriptorsForService(final int serviceIdentifier, - final String characteristicUUID) throws BleError { - final UUID uuid = UUIDConverter.convert(characteristicUUID); - if (uuid == null) { - throw BleErrorUtils.invalidIdentifiers(characteristicUUID); - } + return service.getCharacteristics(); + } - Service service = discoveredServices.get(serviceIdentifier); - if (service == null) { - throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); - } + @Override + public List getCharacteristicsForService(int serviceIdentifier) throws BleError { + Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } + return service.getCharacteristics(); + } - final Characteristic characteristic = service.getCharacteristicByUUID(uuid); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(characteristicUUID); - } + @Override + public List descriptorsForDevice(final String deviceIdentifier, + final String serviceUUID, + final String characteristicUUID) throws BleError { + final UUID[] uuids = UUIDConverter.convert(serviceUUID, characteristicUUID); + if (uuids == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID); + } - return characteristic.getDescriptors(); + Device device = getDeviceById(deviceIdentifier); + + final Service service = device.getServiceByUUID(uuids[0]); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); } - @Override - public List descriptorsForCharacteristic(final int characteristicIdentifier) throws BleError { - Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); - } + final Characteristic characteristic = service.getCharacteristicByUUID(uuids[1]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + return characteristic.getDescriptors(); + } + + @Override + public List descriptorsForService(final int serviceIdentifier, + final String characteristicUUID) throws BleError { + final UUID uuid = UUIDConverter.convert(characteristicUUID); + if (uuid == null) { + throw BleErrorUtils.invalidIdentifiers(characteristicUUID); + } + + Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } + + final Characteristic characteristic = service.getCharacteristicByUUID(uuid); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } + + return characteristic.getDescriptors(); + } - return characteristic.getDescriptors(); + @Override + public List descriptorsForCharacteristic(final int characteristicIdentifier) throws BleError { + Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); } - @Override - public void readCharacteristicForDevice(String deviceIdentifier, - String serviceUUID, - String characteristicUUID, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } + return characteristic.getDescriptors(); + } - safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + @Override + public void readCharacteristicForDevice(String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; } - @Override - public void readCharacteristicForService(int serviceIdentifier, - String characteristicUUID, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - serviceIdentifier, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } - safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + @Override + public void readCharacteristicForService(int serviceIdentifier, + String characteristicUUID, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; } - @Override - public void readCharacteristic(int characteristicIdentifier, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); - if (characteristic == null) { - return; - } + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } - safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + @Override + public void readCharacteristic(int characteristicIdentifier, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; } - @Override - public void writeCharacteristicForDevice(String deviceIdentifier, + safeReadCharacteristicForDevice(characteristic, transactionId, onSuccessCallback, onErrorCallback); + } + + @Override + public void writeCharacteristicForDevice(String deviceIdentifier, + String serviceUUID, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void writeCharacteristicForService(int serviceIdentifier, + String characteristicUUID, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + @Override + public void writeCharacteristic(int characteristicIdentifier, + String valueBase64, + boolean withResponse, + String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; + } + + writeCharacteristicWithValue( + characteristic, + valueBase64, + withResponse, + transactionId, + onSuccessCallback, + onErrorCallback + ); + } + + @Override + public void monitorCharacteristicForDevice(String deviceIdentifier, String serviceUUID, String characteristicUUID, - String valueBase64, - boolean withResponse, String transactionId, - OnSuccessCallback onSuccessCallback, + OnEventCallback onEventCallback, OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } - - writeCharacteristicWithValue( - characteristic, - valueBase64, - withResponse, - transactionId, - onSuccessCallback, - onErrorCallback); + final Characteristic characteristic = getCharacteristicOrEmitError( + deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; } - @Override - public void writeCharacteristicForService(int serviceIdentifier, + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } + + @Override + public void monitorCharacteristicForService(int serviceIdentifier, String characteristicUUID, - String valueBase64, - boolean withResponse, String transactionId, - OnSuccessCallback onSuccessCallback, + OnEventCallback onEventCallback, OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - serviceIdentifier, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } - - writeCharacteristicWithValue( - characteristic, - valueBase64, - withResponse, - transactionId, - onSuccessCallback, - onErrorCallback); - } - - @Override - public void writeCharacteristic(int characteristicIdentifier, - String valueBase64, - boolean withResponse, - String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); - if (characteristic == null) { - return; - } - - writeCharacteristicWithValue( - characteristic, - valueBase64, - withResponse, - transactionId, - onSuccessCallback, - onErrorCallback - ); - } - - @Override - public void monitorCharacteristicForDevice(String deviceIdentifier, - String serviceUUID, - String characteristicUUID, - String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - deviceIdentifier, serviceUUID, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } - - safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + final Characteristic characteristic = getCharacteristicOrEmitError( + serviceIdentifier, characteristicUUID, onErrorCallback); + if (characteristic == null) { + return; } - @Override - public void monitorCharacteristicForService(int serviceIdentifier, - String characteristicUUID, - String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError( - serviceIdentifier, characteristicUUID, onErrorCallback); - if (characteristic == null) { - return; - } - - safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); - } - - @Override - public void monitorCharacteristic(int characteristicIdentifier, String transactionId, - OnEventCallback onEventCallback, - OnErrorCallback onErrorCallback) { - final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); - if (characteristic == null) { - return; - } + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } - safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); - } + @Override + public void monitorCharacteristic(int characteristicIdentifier, String transactionId, + OnEventCallback onEventCallback, + OnErrorCallback onErrorCallback) { + final Characteristic characteristic = getCharacteristicOrEmitError(characteristicIdentifier, onErrorCallback); + if (characteristic == null) { + return; + } + + safeMonitorCharacteristicForDevice(characteristic, transactionId, onEventCallback, onErrorCallback); + } + + @Override + public void readDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + + try { + Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptorForService(final int serviceIdentifier, + final String characteristicUUID, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { - @Override - public void readDescriptorForDevice(final String deviceId, - final String serviceUUID, + try { + Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); + safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void readDescriptor(final int descriptorIdentifier, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + try { + Descriptor descriptor = getDescriptor(descriptorIdentifier); + safeReadDescriptorForDevice(descriptor, transactionId, onSuccessCallback, onErrorCallback); + } catch (BleError error) { + onErrorCallback.onError(error); + } + } + + private void safeReadDescriptorForDevice(final Descriptor descriptor, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .readDescriptor(descriptor.getNativeDescriptor()) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + descriptor.logValue("Read from", bytes); + descriptor.setValue(bytes); + safeExecutor.success(new Descriptor(descriptor)); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); + + pendingTransactions.replaceSubscription(transactionId, subscription); + } + + @Override + public void writeDescriptorForDevice(final String deviceId, + final String serviceUUID, + final String characteristicUUID, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptorForService(final int serviceIdentifier, final String characteristicUUID, final String descriptorUUID, + final String valueBase64, final String transactionId, OnSuccessCallback successCallback, OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptorForCharacteristic(final int characteristicIdentifier, + final String descriptorUUID, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + } + + @Override + public void writeDescriptor(final int descriptorIdentifier, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + try { + Descriptor descriptor = getDescriptor(descriptorIdentifier); + safeWriteDescriptorForDevice( + descriptor, + valueBase64, + transactionId, + successCallback, + errorCallback); + } catch (BleError error) { + errorCallback.onError(error); + } + + } + + private void safeWriteDescriptorForDevice(final Descriptor descriptor, + final String valueBase64, + final String transactionId, + OnSuccessCallback successCallback, + OnErrorCallback errorCallback) { + BluetoothGattDescriptor nativeDescriptor = descriptor.getNativeDescriptor(); + + if (nativeDescriptor.getUuid().equals(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID)) { + errorCallback.onError(BleErrorUtils.descriptorWriteNotAllowed(UUIDConverter.fromUUID(nativeDescriptor.getUuid()))); + return; + } + + final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), errorCallback); + if (connection == null) { + return; + } + + final byte[] value; + try { + value = Base64Converter.decode(valueBase64); + } catch (Throwable e) { + String uuid = UUIDConverter.fromUUID(nativeDescriptor.getUuid()); + errorCallback.onError(BleErrorUtils.invalidWriteDataForDescriptor(valueBase64, uuid)); + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(successCallback, errorCallback); + + final Disposable subscription = connection + .writeDescriptor(nativeDescriptor, value) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(() -> { + descriptor.logValue("Write to", value); + descriptor.setValue(value); + safeExecutor.success(new Descriptor(descriptor)); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); - try { - Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); - safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - @Override - public void readDescriptorForService(final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - try { - Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); - safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } - } + // Mark: Descriptors getters ------------------------------------------------------------------- - @Override - public void readDescriptorForCharacteristic(final int characteristicIdentifier, - final String descriptorUUID, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - - try { - Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); - safeReadDescriptorForDevice(descriptor, transactionId, successCallback, errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } + private Descriptor getDescriptor(@NonNull final String deviceId, + @NonNull final String serviceUUID, + @NonNull final String characteristicUUID, + @NonNull final String descriptorUUID) throws BleError { + final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID, descriptorUUID); + if (UUIDs == null) { + throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID, descriptorUUID); } - @Override - public void readDescriptor(final int descriptorIdentifier, - final String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - try { - Descriptor descriptor = getDescriptor(descriptorIdentifier); - safeReadDescriptorForDevice(descriptor, transactionId, onSuccessCallback, onErrorCallback); - } catch (BleError error) { - onErrorCallback.onError(error); - } + final Device device = connectedDevices.get(deviceId); + if (device == null) { + throw BleErrorUtils.deviceNotConnected(deviceId); } - private void safeReadDescriptorForDevice(final Descriptor descriptor, - final String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .readDescriptor(descriptor.getNativeDescriptor()) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(bytes -> { - descriptor.logValue("Read from", bytes); - descriptor.setValue(bytes); - safeExecutor.success(new Descriptor(descriptor)); - pendingTransactions.removeSubscription(transactionId); - }, error -> { - safeExecutor.error(errorConverter.toError(error)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - @Override - public void writeDescriptorForDevice(final String deviceId, - final String serviceUUID, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - try { - Descriptor descriptor = getDescriptor(deviceId, serviceUUID, characteristicUUID, descriptorUUID); - safeWriteDescriptorForDevice( - descriptor, - valueBase64, - transactionId, - successCallback, - errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } + final Service service = device.getServiceByUUID(UUIDs[0]); + if (service == null) { + throw BleErrorUtils.serviceNotFound(serviceUUID); } - @Override - public void writeDescriptorForService(final int serviceIdentifier, - final String characteristicUUID, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - try { - Descriptor descriptor = getDescriptor(serviceIdentifier, characteristicUUID, descriptorUUID); - safeWriteDescriptorForDevice( - descriptor, - valueBase64, - transactionId, - successCallback, - errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); } - @Override - public void writeDescriptorForCharacteristic(final int characteristicIdentifier, - final String descriptorUUID, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - try { - Descriptor descriptor = getDescriptor(characteristicIdentifier, descriptorUUID); - safeWriteDescriptorForDevice( - descriptor, - valueBase64, - transactionId, - successCallback, - errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } + final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[2]); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); } - @Override - public void writeDescriptor(final int descriptorIdentifier, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - try { - Descriptor descriptor = getDescriptor(descriptorIdentifier); - safeWriteDescriptorForDevice( - descriptor, - valueBase64, - transactionId, - successCallback, - errorCallback); - } catch (BleError error) { - errorCallback.onError(error); - } + return descriptor; + } + private Descriptor getDescriptor(final int serviceIdentifier, + @NonNull final String characteristicUUID, + @NonNull final String descriptorUUID) throws BleError { + final UUID[] UUIDs = UUIDConverter.convert(characteristicUUID, descriptorUUID); + if (UUIDs == null) { + throw BleErrorUtils.invalidIdentifiers(characteristicUUID, descriptorUUID); } - private void safeWriteDescriptorForDevice(final Descriptor descriptor, - final String valueBase64, - final String transactionId, - OnSuccessCallback successCallback, - OnErrorCallback errorCallback) { - BluetoothGattDescriptor nativeDescriptor = descriptor.getNativeDescriptor(); + final Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); + } - if (nativeDescriptor.getUuid().equals(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID)) { - errorCallback.onError(BleErrorUtils.descriptorWriteNotAllowed(UUIDConverter.fromUUID(nativeDescriptor.getUuid()))); - return; - } + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[0]); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(characteristicUUID); + } - final RxBleConnection connection = getConnectionOrEmitError(descriptor.getDeviceId(), errorCallback); - if (connection == null) { - return; - } + final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[1]); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); + } - final byte[] value; - try { - value = Base64Converter.decode(valueBase64); - } catch (Throwable e) { - String uuid = UUIDConverter.fromUUID(nativeDescriptor.getUuid()); - errorCallback.onError(BleErrorUtils.invalidWriteDataForDescriptor(valueBase64, uuid)); - return; - } + return descriptor; + } - final SafeExecutor safeExecutor = new SafeExecutor<>(successCallback, errorCallback); - - final Disposable subscription = connection - .writeDescriptor(nativeDescriptor, value) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(() -> { - descriptor.logValue("Write to", value); - descriptor.setValue(value); - safeExecutor.success(new Descriptor(descriptor)); - pendingTransactions.removeSubscription(transactionId); - }, error -> { - safeExecutor.error(errorConverter.toError(error)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - // Mark: Descriptors getters ------------------------------------------------------------------- - - private Descriptor getDescriptor(@NonNull final String deviceId, - @NonNull final String serviceUUID, - @NonNull final String characteristicUUID, - @NonNull final String descriptorUUID) throws BleError { - final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID, descriptorUUID); - if (UUIDs == null) { - throw BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID, descriptorUUID); - } + private Descriptor getDescriptor(final int characteristicIdentifier, + @NonNull final String descriptorUUID) throws BleError { + final UUID uuid = UUIDConverter.convert(descriptorUUID); + if (uuid == null) { + throw BleErrorUtils.invalidIdentifiers(descriptorUUID); + } - final Device device = connectedDevices.get(deviceId); - if (device == null) { - throw BleErrorUtils.deviceNotConnected(deviceId); - } + final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); + } - final Service service = device.getServiceByUUID(UUIDs[0]); - if (service == null) { - throw BleErrorUtils.serviceNotFound(serviceUUID); - } + final Descriptor descriptor = characteristic.getDescriptorByUUID(uuid); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(descriptorUUID); + } - final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(characteristicUUID); - } + return descriptor; + } - final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[2]); - if (descriptor == null) { - throw BleErrorUtils.descriptorNotFound(descriptorUUID); - } + private Descriptor getDescriptor(final int descriptorIdentifier) throws BleError { - return descriptor; + final Descriptor descriptor = discoveredDescriptors.get(descriptorIdentifier); + if (descriptor == null) { + throw BleErrorUtils.descriptorNotFound(Integer.toString(descriptorIdentifier)); } - private Descriptor getDescriptor(final int serviceIdentifier, - @NonNull final String characteristicUUID, - @NonNull final String descriptorUUID) throws BleError { - final UUID[] UUIDs = UUIDConverter.convert(characteristicUUID, descriptorUUID); - if (UUIDs == null) { - throw BleErrorUtils.invalidIdentifiers(characteristicUUID, descriptorUUID); - } + return descriptor; + } - final Service service = discoveredServices.get(serviceIdentifier); - if (service == null) { - throw BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier)); - } + @Override + public void cancelTransaction(String transactionId) { + pendingTransactions.removeSubscription(transactionId); + } - final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[0]); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(characteristicUUID); - } + public void setLogLevel(String logLevel) { + currentLogLevel = LogLevel.toLogLevel(logLevel); + RxBleLog.setLogLevel(currentLogLevel); + } - final Descriptor descriptor = characteristic.getDescriptorByUUID(UUIDs[1]); - if (descriptor == null) { - throw BleErrorUtils.descriptorNotFound(descriptorUUID); - } + @Override + public String getLogLevel() { + return LogLevel.fromLogLevel(currentLogLevel); + } - return descriptor; + private Disposable monitorAdapterStateChanges(Context context, + final OnEventCallback onAdapterStateChangeCallback) { + if (!supportsBluetoothLowEnergy()) { + return null; } - private Descriptor getDescriptor(final int characteristicIdentifier, - @NonNull final String descriptorUUID) throws BleError { - final UUID uuid = UUIDConverter.convert(descriptorUUID); - if (uuid == null) { - throw BleErrorUtils.invalidIdentifiers(descriptorUUID); - } - - final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); - if (characteristic == null) { - throw BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier)); - } + return new RxBleAdapterStateObservable(context) + .map(this::mapRxBleAdapterStateToLocalBluetoothState) + .subscribe(onAdapterStateChangeCallback::onEvent); + } - final Descriptor descriptor = characteristic.getDescriptorByUUID(uuid); - if (descriptor == null) { - throw BleErrorUtils.descriptorNotFound(descriptorUUID); - } + private boolean supportsBluetoothLowEnergy() { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); + } - return descriptor; + @BluetoothState + private String mapRxBleAdapterStateToLocalBluetoothState( + RxBleAdapterStateObservable.BleAdapterState rxBleAdapterState + ) { + if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { + return BluetoothState.POWERED_ON; + } else if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_OFF) { + return BluetoothState.POWERED_OFF; + } else { + return BluetoothState.RESETTING; } + } - private Descriptor getDescriptor(final int descriptorIdentifier) throws BleError { - - final Descriptor descriptor = discoveredDescriptors.get(descriptorIdentifier); - if (descriptor == null) { - throw BleErrorUtils.descriptorNotFound(Integer.toString(descriptorIdentifier)); - } - - return descriptor; + @SuppressLint("MissingPermission") + private void changeAdapterState(final RxBleAdapterStateObservable.BleAdapterState desiredAdapterState, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + if (bluetoothManager == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothStateChangeFailed, "BluetoothManager is null", null)); + return; } - @Override - public void cancelTransaction(String transactionId) { - pendingTransactions.removeSubscription(transactionId); - } + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - public void setLogLevel(String logLevel) { - currentLogLevel = LogLevel.toLogLevel(logLevel); - RxBleLog.setLogLevel(currentLogLevel); - } + final Disposable subscription = new RxBleAdapterStateObservable(context) + .takeUntil(actualAdapterState -> desiredAdapterState == actualAdapterState) + .firstOrError() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(state -> { + safeExecutor.success(null); + pendingTransactions.removeSubscription(transactionId); + }, error -> { + safeExecutor.error(errorConverter.toError(error)); + pendingTransactions.removeSubscription(transactionId); + }); - @Override - public String getLogLevel() { - return LogLevel.fromLogLevel(currentLogLevel); - } - private Disposable monitorAdapterStateChanges(Context context, - final OnEventCallback onAdapterStateChangeCallback) { - if (!supportsBluetoothLowEnergy()) { - return null; + boolean desiredAndInitialStateAreSame = false; + try { + if (desiredAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (context instanceof Activity) { + ((Activity) context).startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1); + desiredAndInitialStateAreSame = true; + } + } else { + desiredAndInitialStateAreSame = !bluetoothAdapter.enable(); + } + } else { + desiredAndInitialStateAreSame = !bluetoothAdapter.disable(); + } + } catch (SecurityException e) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothUnauthorized, + "Method requires BLUETOOTH_CONNECT permission", + null) + ); + } else { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothUnauthorized, + "Method requires BLUETOOTH_ADMIN permission", + null) + ); + } + } catch (Exception e) { + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothStateChangeFailed, + String.format("Couldn't set bluetooth adapter state because of: %s", e.getMessage() != null ? e.getMessage() : "unknown error"), + null) + ); + } + if (desiredAndInitialStateAreSame) { + subscription.dispose(); + onErrorCallback.onError(new BleError( + BleErrorCode.BluetoothStateChangeFailed, + String.format("Couldn't set bluetooth adapter state to %s", desiredAdapterState.toString()), + null)); + } else { + pendingTransactions.replaceSubscription(transactionId, subscription); + } + } + + @BluetoothState + private String mapNativeAdapterStateToLocalBluetoothState(int adapterState) { + switch (adapterState) { + case BluetoothAdapter.STATE_OFF: + return BluetoothState.POWERED_OFF; + case BluetoothAdapter.STATE_ON: + return BluetoothState.POWERED_ON; + case BluetoothAdapter.STATE_TURNING_OFF: + // fallthrough + case BluetoothAdapter.STATE_TURNING_ON: + return BluetoothState.RESETTING; + default: + return BluetoothState.UNKNOWN; + } + } + + private void safeStartDeviceScan(final UUID[] uuids, + final int scanMode, + final int callbackType, + final boolean legacyScan, + final OnEventCallback onEventCallback, + final OnErrorCallback onErrorCallback) { + if (rxBleClient == null) { + onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to start device scan", null)); + return; + } + + ScanSettings scanSettings = new ScanSettings.Builder() + .setScanMode(scanMode) + .setCallbackType(callbackType) + .setLegacy(legacyScan) + .build(); + + int length = uuids == null ? 0 : uuids.length; + ScanFilter[] filters = new ScanFilter[length]; + for (int i = 0; i < length; i++) { + filters[i] = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuids[i].toString())).build(); + } + + scanSubscription = rxBleClient + .scanBleDevices(scanSettings, filters) + .subscribe(scanResult -> { + String deviceId = scanResult.getBleDevice().getMacAddress(); + if (!discoveredDevices.containsKey(deviceId)) { + discoveredDevices.put(deviceId, rxBleDeviceToDeviceMapper.map(scanResult.getBleDevice(), null)); + } + onEventCallback.onEvent(rxScanResultToScanResultMapper.map(scanResult)); + }, throwable -> onErrorCallback.onError(errorConverter.toError(throwable))); + } + + @NonNull + private Device getDeviceById(@NonNull final String deviceId) throws BleError { + final Device device = connectedDevices.get(deviceId); + if (device == null) { + throw BleErrorUtils.deviceNotConnected(deviceId); + } + return device; + } + + @Nullable + private RxBleConnection getConnectionOrEmitError(@NonNull final String deviceId, + @NonNull OnErrorCallback onErrorCallback) { + final RxBleConnection connection = activeConnections.get(deviceId); + if (connection == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); + return null; + } + return connection; + } + + private void safeConnectToDevice(final RxBleDevice device, + final boolean autoConnect, + final int requestMtu, + final RefreshGattMoment refreshGattMoment, + final Long timeout, + final int connectionPriority, + final OnSuccessCallback onSuccessCallback, + final OnEventCallback onConnectionStateChangedCallback, + final OnErrorCallback onErrorCallback) { + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + Observable connect = device + .establishConnection(autoConnect) + .doOnSubscribe(disposable -> onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTING)) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + onDeviceDisconnected(device); + onConnectionStateChangedCallback.onEvent(ConnectionState.DISCONNECTED); + }); + + if (refreshGattMoment == RefreshGattMoment.ON_CONNECTED) { + connect = connect.flatMap(rxBleConnection -> rxBleConnection + .queue(new RefreshGattCustomOperation()) + .map(refreshGattSuccess -> rxBleConnection)); + } + + if (connectionPriority > 0) { + connect = connect.flatMap(rxBleConnection -> rxBleConnection + .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) + .andThen(Observable.just(rxBleConnection)) + ); + } + + if (requestMtu > 0) { + connect = connect.flatMap(rxBleConnection -> + rxBleConnection.requestMtu(requestMtu) + .map(integer -> rxBleConnection) + .toObservable() + ); + } + + if (timeout != null) { + connect = connect.timeout(timeout, TimeUnit.MILLISECONDS); + } + + + final Disposable subscription = connect + .subscribe(rxBleConnection -> { + Device localDevice = rxBleDeviceToDeviceMapper.map(device, rxBleConnection); + onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTED); + cleanServicesAndCharacteristicsForDevice(localDevice); + connectedDevices.put(device.getMacAddress(), localDevice); + activeConnections.put(device.getMacAddress(), rxBleConnection); + safeExecutor.success(localDevice); + }, error -> { + BleError bleError = errorConverter.toError(error); + safeExecutor.error(bleError); + onDeviceDisconnected(device); + }); + + connectingDevices.replaceSubscription(device.getMacAddress(), subscription); + } + + private void onDeviceDisconnected(RxBleDevice rxDevice) { + activeConnections.remove(rxDevice.getMacAddress()); + Device device = connectedDevices.remove(rxDevice.getMacAddress()); + if (device == null) { + return; + } + + cleanServicesAndCharacteristicsForDevice(device); + connectingDevices.removeSubscription(device.getId()); + } + + private void safeDiscoverAllServicesAndCharacteristicsForDevice(final Device device, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); + if (connection == null) { + return; + } + + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); + + final Disposable subscription = connection + .discoverServices() + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(rxBleDeviceServices -> { + ArrayList services = new ArrayList<>(); + for (BluetoothGattService gattService : rxBleDeviceServices.getBluetoothGattServices()) { + Service service = serviceFactory.create(device.getId(), gattService); + discoveredServices.put(service.getId(), service); + services.add(service); + + for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) { + Characteristic characteristic = new Characteristic(service, gattCharacteristic); + discoveredCharacteristics.put(characteristic.getId(), characteristic); + + for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { + Descriptor descriptor = new Descriptor(characteristic, gattDescriptor); + discoveredDescriptors.put(descriptor.getId(), descriptor); + } + } } + device.setServices(services); + // Moved from onSuccess method from old RxJava1 implementation + safeExecutor.success(device); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); - return new RxBleAdapterStateObservable(context) - .map(this::mapRxBleAdapterStateToLocalBluetoothState) - .subscribe(onAdapterStateChangeCallback::onEvent); - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - private boolean supportsBluetoothLowEnergy() { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); + private void safeReadCharacteristicForDevice(final Characteristic characteristic, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; } - @BluetoothState - private String mapRxBleAdapterStateToLocalBluetoothState( - RxBleAdapterStateObservable.BleAdapterState rxBleAdapterState - ) { - if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { - return BluetoothState.POWERED_ON; - } else if (rxBleAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_OFF) { - return BluetoothState.POWERED_OFF; - } else { - return BluetoothState.RESETTING; - } - } + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - @SuppressLint("MissingPermission") - private void changeAdapterState(final RxBleAdapterStateObservable.BleAdapterState desiredAdapterState, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - if (bluetoothManager == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothStateChangeFailed, "BluetoothManager is null", null)); - return; - } + final Disposable subscription = connection + .readCharacteristic(characteristic.gattCharacteristic) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + characteristic.logValue("Read from", bytes); + characteristic.setValue(bytes); + safeExecutor.success(new Characteristic(characteristic)); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = new RxBleAdapterStateObservable(context) - .takeUntil(actualAdapterState -> desiredAdapterState == actualAdapterState) - .firstOrError() - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(state -> { - safeExecutor.success(null); - pendingTransactions.removeSubscription(transactionId); - }, error -> { - safeExecutor.error(errorConverter.toError(error)); - pendingTransactions.removeSubscription(transactionId); - }); - - - boolean desiredAndInitialStateAreSame = false; - try { - if (desiredAdapterState == RxBleAdapterStateObservable.BleAdapterState.STATE_ON) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - if (context instanceof Activity) { - ((Activity) context).startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1); - desiredAndInitialStateAreSame = true; - } - } else { - desiredAndInitialStateAreSame = !bluetoothAdapter.enable(); - } - } else { - desiredAndInitialStateAreSame = !bluetoothAdapter.disable(); - } - } catch (SecurityException e) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - onErrorCallback.onError(new BleError( - BleErrorCode.BluetoothUnauthorized, - "Method requires BLUETOOTH_CONNECT permission", - null) - ); - } else { - onErrorCallback.onError(new BleError( - BleErrorCode.BluetoothUnauthorized, - "Method requires BLUETOOTH_ADMIN permission", - null) - ); - } - } catch (Exception e) { - onErrorCallback.onError(new BleError( - BleErrorCode.BluetoothStateChangeFailed, - String.format("Couldn't set bluetooth adapter state because of: %s", e.getMessage() != null ? e.getMessage() : "unknown error"), - null) - ); - } - if (desiredAndInitialStateAreSame) { - subscription.dispose(); - onErrorCallback.onError(new BleError( - BleErrorCode.BluetoothStateChangeFailed, - String.format("Couldn't set bluetooth adapter state to %s", desiredAdapterState.toString()), - null)); - } else { - pendingTransactions.replaceSubscription(transactionId, subscription); - } - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - @BluetoothState - private String mapNativeAdapterStateToLocalBluetoothState(int adapterState) { - switch (adapterState) { - case BluetoothAdapter.STATE_OFF: - return BluetoothState.POWERED_OFF; - case BluetoothAdapter.STATE_ON: - return BluetoothState.POWERED_ON; - case BluetoothAdapter.STATE_TURNING_OFF: - // fallthrough - case BluetoothAdapter.STATE_TURNING_ON: - return BluetoothState.RESETTING; - default: - return BluetoothState.UNKNOWN; - } + private void writeCharacteristicWithValue(final Characteristic characteristic, + final String valueBase64, + final Boolean response, + final String transactionId, + OnSuccessCallback onSuccessCallback, + OnErrorCallback onErrorCallback) { + final byte[] value; + try { + value = Base64Converter.decode(valueBase64); + } catch (Throwable error) { + onErrorCallback.onError( + BleErrorUtils.invalidWriteDataForCharacteristic(valueBase64, + UUIDConverter.fromUUID(characteristic.getUuid()))); + return; + } + + characteristic.setWriteType(response ? + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT : + BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); + + safeWriteCharacteristicForDevice( + characteristic, + value, + transactionId, + onSuccessCallback, + onErrorCallback); + } + + private void safeWriteCharacteristicForDevice(final Characteristic characteristic, + final byte[] value, + final String transactionId, + final OnSuccessCallback onSuccessCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; } - private void safeStartDeviceScan(final UUID[] uuids, - final int scanMode, - final int callbackType, - final boolean legacyScan, - final OnEventCallback onEventCallback, - final OnErrorCallback onErrorCallback) { - if (rxBleClient == null) { - onErrorCallback.onError(new BleError(BleErrorCode.BluetoothManagerDestroyed, "BleManager not created when tried to start device scan", null)); - return; - } + final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - ScanSettings scanSettings = new ScanSettings.Builder() - .setScanMode(scanMode) - .setCallbackType(callbackType) - .setLegacy(legacyScan) - .build(); + final Disposable subscription = connection + .writeCharacteristic(characteristic.gattCharacteristic, value) + .doOnDispose(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .subscribe(bytes -> { + characteristic.logValue("Write to", bytes); + characteristic.setValue(bytes); + safeExecutor.success(new Characteristic(characteristic)); + pendingTransactions.removeSubscription(transactionId); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); - int length = uuids == null ? 0 : uuids.length; - ScanFilter[] filters = new ScanFilter[length]; - for (int i = 0; i < length; i++) { - filters[i] = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuids[i].toString())).build(); - } + pendingTransactions.replaceSubscription(transactionId, subscription); + } - scanSubscription = rxBleClient - .scanBleDevices(scanSettings, filters) - .subscribe(scanResult -> { - String deviceId = scanResult.getBleDevice().getMacAddress(); - if (!discoveredDevices.containsKey(deviceId)) { - discoveredDevices.put(deviceId, rxBleDeviceToDeviceMapper.map(scanResult.getBleDevice(), null)); - } - onEventCallback.onEvent(rxScanResultToScanResultMapper.map(scanResult)); - }, throwable -> onErrorCallback.onError(errorConverter.toError(throwable))); - } - - @NonNull - private Device getDeviceById(@NonNull final String deviceId) throws BleError { - final Device device = connectedDevices.get(deviceId); - if (device == null) { - throw BleErrorUtils.deviceNotConnected(deviceId); - } - return device; + private void safeMonitorCharacteristicForDevice(final Characteristic characteristic, + final String transactionId, + final OnEventCallback onEventCallback, + final OnErrorCallback onErrorCallback) { + final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); + if (connection == null) { + return; } - @Nullable - private RxBleConnection getConnectionOrEmitError(@NonNull final String deviceId, - @NonNull OnErrorCallback onErrorCallback) { - final RxBleConnection connection = activeConnections.get(deviceId); - if (connection == null) { - onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); - return null; - } - return connection; - } - - private void safeConnectToDevice(final RxBleDevice device, - final boolean autoConnect, - final int requestMtu, - final RefreshGattMoment refreshGattMoment, - final Long timeout, - final int connectionPriority, - final OnSuccessCallback onSuccessCallback, - final OnEventCallback onConnectionStateChangedCallback, - final OnErrorCallback onErrorCallback) { - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - Observable connect = device - .establishConnection(autoConnect) - .doOnSubscribe(disposable -> onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTING)) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - onDeviceDisconnected(device); - onConnectionStateChangedCallback.onEvent(ConnectionState.DISCONNECTED); - }); - - if (refreshGattMoment == RefreshGattMoment.ON_CONNECTED) { - connect = connect.flatMap(rxBleConnection -> rxBleConnection - .queue(new RefreshGattCustomOperation()) - .map(refreshGattSuccess -> rxBleConnection)); - } + final SafeExecutor safeExecutor = new SafeExecutor<>(null, onErrorCallback); - if (connectionPriority > 0) { - connect = connect.flatMap(rxBleConnection -> rxBleConnection - .requestConnectionPriority(connectionPriority, 1, TimeUnit.MILLISECONDS) - .andThen(Observable.just(rxBleConnection)) - ); + final Disposable subscription = Observable.defer(() -> { + BluetoothGattDescriptor cccDescriptor = characteristic.getGattDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); + NotificationSetupMode setupMode = cccDescriptor != null + ? NotificationSetupMode.QUICK_SETUP + : NotificationSetupMode.COMPAT; + if (characteristic.isNotifiable()) { + return connection.setupNotification(characteristic.gattCharacteristic, setupMode); } - if (requestMtu > 0) { - connect = connect.flatMap(rxBleConnection -> - rxBleConnection.requestMtu(requestMtu) - .map(integer -> rxBleConnection) - .toObservable() - ); + if (characteristic.isIndicatable()) { + return connection.setupIndication(characteristic.gattCharacteristic, setupMode); } - if (timeout != null) { - connect = connect.timeout(timeout, TimeUnit.MILLISECONDS); - } + return Observable.error(new CannotMonitorCharacteristicException(characteristic)); + }) + .flatMap(observable -> observable) + .toFlowable(BackpressureStrategy.BUFFER) + .observeOn(Schedulers.computation()) + .doOnCancel(() -> { + safeExecutor.error(BleErrorUtils.cancelled()); + pendingTransactions.removeSubscription(transactionId); + }) + .doOnComplete(() -> pendingTransactions.removeSubscription(transactionId)) + .subscribe(bytes -> { + characteristic.logValue("Notification from", bytes); + characteristic.setValue(bytes); + onEventCallback.onEvent(new Characteristic(characteristic)); + }, throwable -> { + safeExecutor.error(errorConverter.toError(throwable)); + pendingTransactions.removeSubscription(transactionId); + }); + pendingTransactions.replaceSubscription(transactionId, subscription); + } - final Disposable subscription = connect - .subscribe(rxBleConnection -> { - Device localDevice = rxBleDeviceToDeviceMapper.map(device, rxBleConnection); - onConnectionStateChangedCallback.onEvent(ConnectionState.CONNECTED); - cleanServicesAndCharacteristicsForDevice(localDevice); - connectedDevices.put(device.getMacAddress(), localDevice); - activeConnections.put(device.getMacAddress(), rxBleConnection); - safeExecutor.success(localDevice); - }, error -> { - BleError bleError = errorConverter.toError(error); - safeExecutor.error(bleError); - onDeviceDisconnected(device); - }); - - connectingDevices.replaceSubscription(device.getMacAddress(), subscription); - } - - private void onDeviceDisconnected(RxBleDevice rxDevice) { - activeConnections.remove(rxDevice.getMacAddress()); - Device device = connectedDevices.remove(rxDevice.getMacAddress()); - if (device == null) { - return; - } + @Nullable + private Characteristic getCharacteristicOrEmitError(@NonNull final String deviceId, + @NonNull final String serviceUUID, + @NonNull final String characteristicUUID, + @NonNull final OnErrorCallback onErrorCallback) { - cleanServicesAndCharacteristicsForDevice(device); - connectingDevices.removeSubscription(device.getId()); + final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID); + if (UUIDs == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID)); + return null; } - private void safeDiscoverAllServicesAndCharacteristicsForDevice(final Device device, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - final RxBleConnection connection = getConnectionOrEmitError(device.getId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .discoverServices() - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(rxBleDeviceServices -> { - ArrayList services = new ArrayList<>(); - for (BluetoothGattService gattService : rxBleDeviceServices.getBluetoothGattServices()) { - Service service = serviceFactory.create(device.getId(), gattService); - discoveredServices.put(service.getId(), service); - services.add(service); - - for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) { - Characteristic characteristic = new Characteristic(service, gattCharacteristic); - discoveredCharacteristics.put(characteristic.getId(), characteristic); - - for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { - Descriptor descriptor = new Descriptor(characteristic, gattDescriptor); - discoveredDescriptors.put(descriptor.getId(), descriptor); - } - } - } - device.setServices(services); - // Moved from onSuccess method from old RxJava1 implementation - safeExecutor.success(device); - pendingTransactions.removeSubscription(transactionId); - }, throwable -> { - safeExecutor.error(errorConverter.toError(throwable)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - private void safeReadCharacteristicForDevice(final Characteristic characteristic, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .readCharacteristic(characteristic.gattCharacteristic) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(bytes -> { - characteristic.logValue("Read from", bytes); - characteristic.setValue(bytes); - safeExecutor.success(new Characteristic(characteristic)); - pendingTransactions.removeSubscription(transactionId); - }, throwable -> { - safeExecutor.error(errorConverter.toError(throwable)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - private void writeCharacteristicWithValue(final Characteristic characteristic, - final String valueBase64, - final Boolean response, - final String transactionId, - OnSuccessCallback onSuccessCallback, - OnErrorCallback onErrorCallback) { - final byte[] value; - try { - value = Base64Converter.decode(valueBase64); - } catch (Throwable error) { - onErrorCallback.onError( - BleErrorUtils.invalidWriteDataForCharacteristic(valueBase64, - UUIDConverter.fromUUID(characteristic.getUuid()))); - return; - } - - characteristic.setWriteType(response ? - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT : - BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); - - safeWriteCharacteristicForDevice( - characteristic, - value, - transactionId, - onSuccessCallback, - onErrorCallback); + final Device device = connectedDevices.get(deviceId); + if (device == null) { + onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); + return null; } - private void safeWriteCharacteristicForDevice(final Characteristic characteristic, - final byte[] value, - final String transactionId, - final OnSuccessCallback onSuccessCallback, - final OnErrorCallback onErrorCallback) { - final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(onSuccessCallback, onErrorCallback); - - final Disposable subscription = connection - .writeCharacteristic(characteristic.gattCharacteristic, value) - .doOnDispose(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .subscribe(bytes -> { - characteristic.logValue("Write to", bytes); - characteristic.setValue(bytes); - safeExecutor.success(new Characteristic(characteristic)); - pendingTransactions.removeSubscription(transactionId); - }, throwable -> { - safeExecutor.error(errorConverter.toError(throwable)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - private void safeMonitorCharacteristicForDevice(final Characteristic characteristic, - final String transactionId, - final OnEventCallback onEventCallback, - final OnErrorCallback onErrorCallback) { - final RxBleConnection connection = getConnectionOrEmitError(characteristic.getDeviceId(), onErrorCallback); - if (connection == null) { - return; - } - - final SafeExecutor safeExecutor = new SafeExecutor<>(null, onErrorCallback); - - final Disposable subscription = Observable.defer(() -> { - BluetoothGattDescriptor cccDescriptor = characteristic.getGattDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); - NotificationSetupMode setupMode = cccDescriptor != null - ? NotificationSetupMode.QUICK_SETUP - : NotificationSetupMode.COMPAT; - if (characteristic.isNotifiable()) { - return connection.setupNotification(characteristic.gattCharacteristic, setupMode); - } - - if (characteristic.isIndicatable()) { - return connection.setupIndication(characteristic.gattCharacteristic, setupMode); - } - - return Observable.error(new CannotMonitorCharacteristicException(characteristic)); - }) - .flatMap(observable -> observable) - .toFlowable(BackpressureStrategy.BUFFER) - .observeOn(Schedulers.computation()) - .doOnCancel(() -> { - safeExecutor.error(BleErrorUtils.cancelled()); - pendingTransactions.removeSubscription(transactionId); - }) - .doOnComplete(() -> pendingTransactions.removeSubscription(transactionId)) - .subscribe(bytes -> { - characteristic.logValue("Notification from", bytes); - characteristic.setValue(bytes); - onEventCallback.onEvent(new Characteristic(characteristic)); - }, throwable -> { - safeExecutor.error(errorConverter.toError(throwable)); - pendingTransactions.removeSubscription(transactionId); - }); - - pendingTransactions.replaceSubscription(transactionId, subscription); - } - - @Nullable - private Characteristic getCharacteristicOrEmitError(@NonNull final String deviceId, - @NonNull final String serviceUUID, - @NonNull final String characteristicUUID, - @NonNull final OnErrorCallback onErrorCallback) { - - final UUID[] UUIDs = UUIDConverter.convert(serviceUUID, characteristicUUID); - if (UUIDs == null) { - onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(serviceUUID, characteristicUUID)); - return null; - } + final Service service = device.getServiceByUUID(UUIDs[0]); + if (service == null) { + onErrorCallback.onError(BleErrorUtils.serviceNotFound(serviceUUID)); + return null; + } - final Device device = connectedDevices.get(deviceId); - if (device == null) { - onErrorCallback.onError(BleErrorUtils.deviceNotConnected(deviceId)); - return null; - } + final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); + return null; + } - final Service service = device.getServiceByUUID(UUIDs[0]); - if (service == null) { - onErrorCallback.onError(BleErrorUtils.serviceNotFound(serviceUUID)); - return null; - } + return characteristic; + } - final Characteristic characteristic = service.getCharacteristicByUUID(UUIDs[1]); - if (characteristic == null) { - onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); - return null; - } + @Nullable + private Characteristic getCharacteristicOrEmitError(final int serviceIdentifier, + @NonNull final String characteristicUUID, + @NonNull final OnErrorCallback onErrorCallback) { - return characteristic; + final UUID uuid = UUIDConverter.convert(characteristicUUID); + if (uuid == null) { + onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(characteristicUUID)); + return null; } - @Nullable - private Characteristic getCharacteristicOrEmitError(final int serviceIdentifier, - @NonNull final String characteristicUUID, - @NonNull final OnErrorCallback onErrorCallback) { + final Service service = discoveredServices.get(serviceIdentifier); + if (service == null) { + onErrorCallback.onError(BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier))); + return null; + } - final UUID uuid = UUIDConverter.convert(characteristicUUID); - if (uuid == null) { - onErrorCallback.onError(BleErrorUtils.invalidIdentifiers(characteristicUUID)); - return null; - } + final Characteristic characteristic = service.getCharacteristicByUUID(uuid); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); + return null; + } - final Service service = discoveredServices.get(serviceIdentifier); - if (service == null) { - onErrorCallback.onError(BleErrorUtils.serviceNotFound(Integer.toString(serviceIdentifier))); - return null; - } + return characteristic; + } - final Characteristic characteristic = service.getCharacteristicByUUID(uuid); - if (characteristic == null) { - onErrorCallback.onError(BleErrorUtils.characteristicNotFound(characteristicUUID)); - return null; - } + @Nullable + private Characteristic getCharacteristicOrEmitError(final int characteristicIdentifier, + @NonNull final OnErrorCallback onErrorCallback) { - return characteristic; + final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); + if (characteristic == null) { + onErrorCallback.onError(BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier))); + return null; } - @Nullable - private Characteristic getCharacteristicOrEmitError(final int characteristicIdentifier, - @NonNull final OnErrorCallback onErrorCallback) { + return characteristic; + } - final Characteristic characteristic = discoveredCharacteristics.get(characteristicIdentifier); - if (characteristic == null) { - onErrorCallback.onError(BleErrorUtils.characteristicNotFound(Integer.toString(characteristicIdentifier))); - return null; - } + private void cleanServicesAndCharacteristicsForDevice(@NonNull Device device) { + for (int i = discoveredServices.size() - 1; i >= 0; i--) { + int key = discoveredServices.keyAt(i); + Service service = discoveredServices.get(key); - return characteristic; + if (service.getDeviceID().equals(device.getId())) { + discoveredServices.remove(key); + } } + for (int i = discoveredCharacteristics.size() - 1; i >= 0; i--) { + int key = discoveredCharacteristics.keyAt(i); + Characteristic characteristic = discoveredCharacteristics.get(key); - private void cleanServicesAndCharacteristicsForDevice(@NonNull Device device) { - for (int i = discoveredServices.size() - 1; i >= 0; i--) { - int key = discoveredServices.keyAt(i); - Service service = discoveredServices.get(key); - - if (service.getDeviceID().equals(device.getId())) { - discoveredServices.remove(key); - } - } - for (int i = discoveredCharacteristics.size() - 1; i >= 0; i--) { - int key = discoveredCharacteristics.keyAt(i); - Characteristic characteristic = discoveredCharacteristics.get(key); - - if (characteristic.getDeviceId().equals(device.getId())) { - discoveredCharacteristics.remove(key); - } - } + if (characteristic.getDeviceId().equals(device.getId())) { + discoveredCharacteristics.remove(key); + } + } - for (int i = discoveredDescriptors.size() - 1; i >= 0; i--) { - int key = discoveredDescriptors.keyAt(i); - Descriptor descriptor = discoveredDescriptors.get(key); - if (descriptor.getDeviceId().equals(device.getId())) { - discoveredDescriptors.remove(key); - } - } + for (int i = discoveredDescriptors.size() - 1; i >= 0; i--) { + int key = discoveredDescriptors.keyAt(i); + Descriptor descriptor = discoveredDescriptors.get(key); + if (descriptor.getDeviceId().equals(device.getId())) { + discoveredDescriptors.remove(key); + } } + } } diff --git a/android/src/main/java/com/bleplx/adapter/Characteristic.java b/android/src/main/java/com/bleplx/adapter/Characteristic.java index 1e1fb7a8..7208813a 100755 --- a/android/src/main/java/com/bleplx/adapter/Characteristic.java +++ b/android/src/main/java/com/bleplx/adapter/Characteristic.java @@ -16,136 +16,138 @@ import java.util.List; import java.util.UUID; -/** @noinspection ALL*/ +/** + * @noinspection ALL + */ public class Characteristic { - final private int id; - final private int serviceID; - final private UUID serviceUUID; - final private String deviceID; - private byte[] value; - final BluetoothGattCharacteristic gattCharacteristic; - - public void setValue(byte[] value) { - this.value = value; - } - - public Characteristic(@NonNull Service service, @NonNull BluetoothGattCharacteristic gattCharacteristic) { - this.deviceID = service.getDeviceID(); - this.serviceUUID = service.getUuid(); - this.serviceID = service.getId(); - this.gattCharacteristic = gattCharacteristic; - this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceID, gattCharacteristic.getUuid(), gattCharacteristic.getInstanceId())); - } - - public Characteristic(int id, @NonNull Service service, BluetoothGattCharacteristic gattCharacteristic) { - this.id = id; - this.deviceID = service.getDeviceID(); - this.serviceUUID = service.getUuid(); - this.serviceID = service.getId(); - this.gattCharacteristic = gattCharacteristic; - } - - public Characteristic(Characteristic other) { - id = other.id; - serviceID = other.serviceID; - serviceUUID = other.serviceUUID; - deviceID = other.deviceID; - if (other.value != null) value = other.value.clone(); - gattCharacteristic = other.gattCharacteristic; - } - - public int getId() { - return this.id; - } - - public UUID getUuid() { - return gattCharacteristic.getUuid(); - } - - public int getServiceID() { - return serviceID; - } - - public UUID getServiceUUID() { - return serviceUUID; - } - - public String getDeviceId() { - return deviceID; - } - - public int getInstanceId() { - return gattCharacteristic.getInstanceId(); - } - - public BluetoothGattDescriptor getGattDescriptor(UUID uuid) { - return gattCharacteristic.getDescriptor(uuid); - } - - public void setWriteType(int writeType) { - gattCharacteristic.setWriteType(writeType); - } - - public boolean isReadable() { - return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0; - } - - public boolean isWritableWithResponse() { - return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0; - } - - public boolean isWritableWithoutResponse() { - return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0; - } - - public boolean isNotifiable() { - return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0; - } - - public List getDescriptors() { - ArrayList descriptors = new ArrayList<>(gattCharacteristic.getDescriptors().size()); - for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { - descriptors.add(new Descriptor(this, gattDescriptor)); - } - return descriptors; - } - - public boolean isNotifying() { - BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); - boolean isNotifying = false; - if (descriptor != null) { - byte[] descriptorValue = descriptor.getValue(); - if (descriptorValue != null) { - isNotifying = (descriptorValue[0] & 0x01) != 0; - } - } - return isNotifying; - } - - public boolean isIndicatable() { - return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0; - } - - public byte[] getValue() { - return value; - } - - @Nullable - public Descriptor getDescriptorByUUID(@NonNull UUID uuid) { - BluetoothGattDescriptor descriptor = this.gattCharacteristic.getDescriptor(uuid); - if (descriptor == null) return null; - return new Descriptor(this, descriptor); - } - - void logValue(String message, byte[] value) { - if (value == null) { - value = gattCharacteristic.getValue(); - } - String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; - RxBleLog.v(message + - " Characteristic(uuid: " + gattCharacteristic.getUuid().toString() + - ", id: " + id + - ", value: " + hexValue + ")"); - } + final private int id; + final private int serviceID; + final private UUID serviceUUID; + final private String deviceID; + private byte[] value; + final BluetoothGattCharacteristic gattCharacteristic; + + public void setValue(byte[] value) { + this.value = value; + } + + public Characteristic(@NonNull Service service, @NonNull BluetoothGattCharacteristic gattCharacteristic) { + this.deviceID = service.getDeviceID(); + this.serviceUUID = service.getUuid(); + this.serviceID = service.getId(); + this.gattCharacteristic = gattCharacteristic; + this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceID, gattCharacteristic.getUuid(), gattCharacteristic.getInstanceId())); + } + + public Characteristic(int id, @NonNull Service service, BluetoothGattCharacteristic gattCharacteristic) { + this.id = id; + this.deviceID = service.getDeviceID(); + this.serviceUUID = service.getUuid(); + this.serviceID = service.getId(); + this.gattCharacteristic = gattCharacteristic; + } + + public Characteristic(Characteristic other) { + id = other.id; + serviceID = other.serviceID; + serviceUUID = other.serviceUUID; + deviceID = other.deviceID; + if (other.value != null) value = other.value.clone(); + gattCharacteristic = other.gattCharacteristic; + } + + public int getId() { + return this.id; + } + + public UUID getUuid() { + return gattCharacteristic.getUuid(); + } + + public int getServiceID() { + return serviceID; + } + + public UUID getServiceUUID() { + return serviceUUID; + } + + public String getDeviceId() { + return deviceID; + } + + public int getInstanceId() { + return gattCharacteristic.getInstanceId(); + } + + public BluetoothGattDescriptor getGattDescriptor(UUID uuid) { + return gattCharacteristic.getDescriptor(uuid); + } + + public void setWriteType(int writeType) { + gattCharacteristic.setWriteType(writeType); + } + + public boolean isReadable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0; + } + + public boolean isWritableWithResponse() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0; + } + + public boolean isWritableWithoutResponse() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0; + } + + public boolean isNotifiable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0; + } + + public List getDescriptors() { + ArrayList descriptors = new ArrayList<>(gattCharacteristic.getDescriptors().size()); + for (BluetoothGattDescriptor gattDescriptor : gattCharacteristic.getDescriptors()) { + descriptors.add(new Descriptor(this, gattDescriptor)); + } + return descriptors; + } + + public boolean isNotifying() { + BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(Constants.CLIENT_CHARACTERISTIC_CONFIG_UUID); + boolean isNotifying = false; + if (descriptor != null) { + byte[] descriptorValue = descriptor.getValue(); + if (descriptorValue != null) { + isNotifying = (descriptorValue[0] & 0x01) != 0; + } + } + return isNotifying; + } + + public boolean isIndicatable() { + return (gattCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0; + } + + public byte[] getValue() { + return value; + } + + @Nullable + public Descriptor getDescriptorByUUID(@NonNull UUID uuid) { + BluetoothGattDescriptor descriptor = this.gattCharacteristic.getDescriptor(uuid); + if (descriptor == null) return null; + return new Descriptor(this, descriptor); + } + + void logValue(String message, byte[] value) { + if (value == null) { + value = gattCharacteristic.getValue(); + } + String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; + RxBleLog.v(message + + " Characteristic(uuid: " + gattCharacteristic.getUuid().toString() + + ", id: " + id + + ", value: " + hexValue + ")"); + } } diff --git a/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java b/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java index 3041d204..51b0044c 100644 --- a/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java +++ b/android/src/main/java/com/bleplx/adapter/ConnectionOptions.java @@ -6,67 +6,67 @@ public class ConnectionOptions { - /** - * Whether to directly connect to the remote device (false) or to automatically connect as soon - * as the remote device becomes available (true). - */ - private final boolean autoConnect; + /** + * Whether to directly connect to the remote device (false) or to automatically connect as soon + * as the remote device becomes available (true). + */ + private final boolean autoConnect; - /** - * Whether MTU size will be negotiated to this value. It is not guaranteed to get it after - * connection is successful. - */ - private final int requestMTU; + /** + * Whether MTU size will be negotiated to this value. It is not guaranteed to get it after + * connection is successful. + */ + private final int requestMTU; - /** - * Whether action will be taken to reset services cache. This option may be useful when a - * peripheral's firmware was updated and it's services/characteristics were - * added/removed/altered. {@link ...} - */ - private final RefreshGattMoment refreshGattMoment; + /** + * Whether action will be taken to reset services cache. This option may be useful when a + * peripheral's firmware was updated and it's services/characteristics were + * added/removed/altered. {@link ...} + */ + private final RefreshGattMoment refreshGattMoment; - /** - * Number of milliseconds after connection is automatically timed out. In case of race condition - * were connection is established right after timeout event, device will be disconnected - * immediately. Time out may happen earlier then specified due to OS specific behavior. - */ - @Nullable - private final Long timeoutInMillis; + /** + * Number of milliseconds after connection is automatically timed out. In case of race condition + * were connection is established right after timeout event, device will be disconnected + * immediately. Time out may happen earlier then specified due to OS specific behavior. + */ + @Nullable + private final Long timeoutInMillis; - @ConnectionPriority - private final int connectionPriority; + @ConnectionPriority + private final int connectionPriority; - public ConnectionOptions(Boolean autoConnect, - int requestMTU, - RefreshGattMoment refreshGattMoment, - @Nullable Long timeoutInMillis, - int connectionPriority) { - this.autoConnect = autoConnect; - this.requestMTU = requestMTU; - this.refreshGattMoment = refreshGattMoment; - this.timeoutInMillis = timeoutInMillis; - this.connectionPriority = connectionPriority; - } + public ConnectionOptions(Boolean autoConnect, + int requestMTU, + RefreshGattMoment refreshGattMoment, + @Nullable Long timeoutInMillis, + int connectionPriority) { + this.autoConnect = autoConnect; + this.requestMTU = requestMTU; + this.refreshGattMoment = refreshGattMoment; + this.timeoutInMillis = timeoutInMillis; + this.connectionPriority = connectionPriority; + } - public Boolean getAutoConnect() { - return autoConnect; - } + public Boolean getAutoConnect() { + return autoConnect; + } - public int getRequestMTU() { - return requestMTU; - } + public int getRequestMTU() { + return requestMTU; + } - public RefreshGattMoment getRefreshGattMoment() { - return refreshGattMoment; - } + public RefreshGattMoment getRefreshGattMoment() { + return refreshGattMoment; + } - @Nullable - public Long getTimeoutInMillis() { - return timeoutInMillis; - } + @Nullable + public Long getTimeoutInMillis() { + return timeoutInMillis; + } - @ConnectionPriority - public int getConnectionPriority() { - return connectionPriority; - } + @ConnectionPriority + public int getConnectionPriority() { + return connectionPriority; + } } diff --git a/android/src/main/java/com/bleplx/adapter/ConnectionState.java b/android/src/main/java/com/bleplx/adapter/ConnectionState.java index b76358e5..56e0ca93 100644 --- a/android/src/main/java/com/bleplx/adapter/ConnectionState.java +++ b/android/src/main/java/com/bleplx/adapter/ConnectionState.java @@ -2,11 +2,11 @@ public enum ConnectionState { - CONNECTING("connecting"), CONNECTED("connected"), DISCONNECTING("disconnecting"), DISCONNECTED("disconnected"); + CONNECTING("connecting"), CONNECTED("connected"), DISCONNECTING("disconnecting"), DISCONNECTED("disconnected"); - public final String value; + public final String value; - ConnectionState(String value) { - this.value = value; - } + ConnectionState(String value) { + this.value = value; + } } diff --git a/android/src/main/java/com/bleplx/adapter/Descriptor.java b/android/src/main/java/com/bleplx/adapter/Descriptor.java index 103cc9aa..5057497d 100644 --- a/android/src/main/java/com/bleplx/adapter/Descriptor.java +++ b/android/src/main/java/com/bleplx/adapter/Descriptor.java @@ -11,105 +11,107 @@ import java.util.UUID; -/** @noinspection unused*/ +/** + * @noinspection unused + */ public class Descriptor { - final private int characteristicId; - final private int serviceId; - final private UUID characteristicUuid; - final private UUID serviceUuid; - final private String deviceId; - final private BluetoothGattDescriptor descriptor; - final private int id; - final private UUID uuid; - private byte[] value = null; - - public Descriptor(@NonNull Characteristic characteristic, @NonNull BluetoothGattDescriptor gattDescriptor) { - this.characteristicId = characteristic.getId(); - this.characteristicUuid = characteristic.getUuid(); - this.serviceId = characteristic.getServiceID(); - this.serviceUuid = characteristic.getServiceUUID(); - this.descriptor = gattDescriptor; - this.deviceId = characteristic.getDeviceId(); - this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, descriptor.getUuid(), characteristicId)); - this.uuid = gattDescriptor.getUuid(); - } - - //secondary constructor, not used by MBA itself, but which can be used by external plugins (eg. BLEmulator) - public Descriptor(int characteristicId, int serviceId, UUID characteristicUuid, UUID serviceUuid, String deviceId, BluetoothGattDescriptor descriptor, int id, UUID uuid) { - this.characteristicId = characteristicId; - this.serviceId = serviceId; - this.characteristicUuid = characteristicUuid; - this.serviceUuid = serviceUuid; - this.deviceId = deviceId; - this.descriptor = descriptor; - this.id = id; - this.uuid = uuid; - } - - public Descriptor(Descriptor other) { - characteristicUuid = other.characteristicUuid; - characteristicId = other.characteristicId; - serviceUuid = other.serviceUuid; - serviceId = other.serviceId; - deviceId = other.deviceId; - descriptor = other.descriptor; - id = other.id; - uuid = other.uuid; - if (other.value != null) value = other.value.clone(); - } - - public int getId() { - return id; - } - - public String getDeviceId() { - return deviceId; - } - - public int getCharacteristicId() { - return characteristicId; - } - - public int getServiceId() { - return serviceId; - } - - public UUID getCharacteristicUuid() { - return characteristicUuid; - } - - public UUID getServiceUuid() { - return serviceUuid; - } - - public UUID getUuid() { - return uuid; - } - - public byte[] getValue() { - return value; - } - - public void setValue(byte[] value) { - this.value = value; - } - - public void setValueFromCache() { - value = descriptor.getValue(); - } - - public BluetoothGattDescriptor getNativeDescriptor() { - return descriptor; - } - - public void logValue(String message, byte[] value) { - if (value == null) { - value = descriptor.getValue(); - } - String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; - RxBleLog.v(message + - " Descriptor(uuid: " + descriptor.getUuid().toString() + - ", id: " + id + - ", value: " + hexValue + ")"); + final private int characteristicId; + final private int serviceId; + final private UUID characteristicUuid; + final private UUID serviceUuid; + final private String deviceId; + final private BluetoothGattDescriptor descriptor; + final private int id; + final private UUID uuid; + private byte[] value = null; + + public Descriptor(@NonNull Characteristic characteristic, @NonNull BluetoothGattDescriptor gattDescriptor) { + this.characteristicId = characteristic.getId(); + this.characteristicUuid = characteristic.getUuid(); + this.serviceId = characteristic.getServiceID(); + this.serviceUuid = characteristic.getServiceUUID(); + this.descriptor = gattDescriptor; + this.deviceId = characteristic.getDeviceId(); + this.id = IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, descriptor.getUuid(), characteristicId)); + this.uuid = gattDescriptor.getUuid(); + } + + //secondary constructor, not used by MBA itself, but which can be used by external plugins (eg. BLEmulator) + public Descriptor(int characteristicId, int serviceId, UUID characteristicUuid, UUID serviceUuid, String deviceId, BluetoothGattDescriptor descriptor, int id, UUID uuid) { + this.characteristicId = characteristicId; + this.serviceId = serviceId; + this.characteristicUuid = characteristicUuid; + this.serviceUuid = serviceUuid; + this.deviceId = deviceId; + this.descriptor = descriptor; + this.id = id; + this.uuid = uuid; + } + + public Descriptor(Descriptor other) { + characteristicUuid = other.characteristicUuid; + characteristicId = other.characteristicId; + serviceUuid = other.serviceUuid; + serviceId = other.serviceId; + deviceId = other.deviceId; + descriptor = other.descriptor; + id = other.id; + uuid = other.uuid; + if (other.value != null) value = other.value.clone(); + } + + public int getId() { + return id; + } + + public String getDeviceId() { + return deviceId; + } + + public int getCharacteristicId() { + return characteristicId; + } + + public int getServiceId() { + return serviceId; + } + + public UUID getCharacteristicUuid() { + return characteristicUuid; + } + + public UUID getServiceUuid() { + return serviceUuid; + } + + public UUID getUuid() { + return uuid; + } + + public byte[] getValue() { + return value; + } + + public void setValue(byte[] value) { + this.value = value; + } + + public void setValueFromCache() { + value = descriptor.getValue(); + } + + public BluetoothGattDescriptor getNativeDescriptor() { + return descriptor; + } + + public void logValue(String message, byte[] value) { + if (value == null) { + value = descriptor.getValue(); } + String hexValue = value != null ? ByteUtils.bytesToHex(value) : "(null)"; + RxBleLog.v(message + + " Descriptor(uuid: " + descriptor.getUuid().toString() + + ", id: " + id + + ", value: " + hexValue + ")"); + } } diff --git a/android/src/main/java/com/bleplx/adapter/Device.java b/android/src/main/java/com/bleplx/adapter/Device.java index 29d8780a..178709e0 100755 --- a/android/src/main/java/com/bleplx/adapter/Device.java +++ b/android/src/main/java/com/bleplx/adapter/Device.java @@ -8,73 +8,73 @@ public class Device { - private String id; - private String name; - @Nullable - private Integer rssi; - @Nullable - private Integer mtu; - @Nullable - private List services; - - public Device(String id, String name) { - this.id = id; - this.name = name; + private String id; + private String name; + @Nullable + private Integer rssi; + @Nullable + private Integer mtu; + @Nullable + private List services; + + public Device(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Nullable + public Integer getRssi() { + return rssi; + } + + public void setRssi(@Nullable Integer rssi) { + this.rssi = rssi; + } + + @Nullable + public Integer getMtu() { + return mtu; + } + + public void setMtu(@Nullable Integer mtu) { + this.mtu = mtu; + } + + @Nullable + public List getServices() { + return services; + } + + public void setServices(@Nullable List services) { + this.services = services; + } + + @Nullable + public Service getServiceByUUID(@NonNull UUID uuid) { + if (services == null) { + return null; } - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Nullable - public Integer getRssi() { - return rssi; - } - - public void setRssi(@Nullable Integer rssi) { - this.rssi = rssi; - } - - @Nullable - public Integer getMtu() { - return mtu; - } - - public void setMtu(@Nullable Integer mtu) { - this.mtu = mtu; - } - - @Nullable - public List getServices() { - return services; - } - - public void setServices(@Nullable List services) { - this.services = services; - } - - @Nullable - public Service getServiceByUUID(@NonNull UUID uuid) { - if (services == null) { - return null; - } - - for (Service service : services) { - if (uuid.equals(service.getUuid())) - return service; - } - return null; + for (Service service : services) { + if (uuid.equals(service.getUuid())) + return service; } + return null; + } } diff --git a/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java b/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java index 8c6d1bba..a073aff4 100644 --- a/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java +++ b/android/src/main/java/com/bleplx/adapter/OnErrorCallback.java @@ -4,5 +4,5 @@ public interface OnErrorCallback { - void onError(BleError error); + void onError(BleError error); } diff --git a/android/src/main/java/com/bleplx/adapter/OnEventCallback.java b/android/src/main/java/com/bleplx/adapter/OnEventCallback.java index 71d378e4..bc6330c8 100644 --- a/android/src/main/java/com/bleplx/adapter/OnEventCallback.java +++ b/android/src/main/java/com/bleplx/adapter/OnEventCallback.java @@ -2,5 +2,5 @@ public interface OnEventCallback { - void onEvent(T data); + void onEvent(T data); } diff --git a/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java b/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java index 1dada60f..27a39691 100644 --- a/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java +++ b/android/src/main/java/com/bleplx/adapter/OnSuccessCallback.java @@ -2,5 +2,5 @@ public interface OnSuccessCallback { - void onSuccess(T data); + void onSuccess(T data); } diff --git a/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java b/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java index 92435684..eafefc07 100755 --- a/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java +++ b/android/src/main/java/com/bleplx/adapter/RefreshGattMoment.java @@ -3,18 +3,18 @@ public enum RefreshGattMoment { - ON_CONNECTED("OnConnected"); + ON_CONNECTED("OnConnected"); - final String name; + final String name; - RefreshGattMoment(String name) { - this.name = name; - } + RefreshGattMoment(String name) { + this.name = name; + } - public static RefreshGattMoment getByName(String name) { - for (RefreshGattMoment refreshGattMoment : RefreshGattMoment.values()) { - if (refreshGattMoment.name.equals(name)) return refreshGattMoment; - } - return null; + public static RefreshGattMoment getByName(String name) { + for (RefreshGattMoment refreshGattMoment : RefreshGattMoment.values()) { + if (refreshGattMoment.name.equals(name)) return refreshGattMoment; } + return null; + } } diff --git a/android/src/main/java/com/bleplx/adapter/ScanResult.java b/android/src/main/java/com/bleplx/adapter/ScanResult.java index c3a89ba0..53eac5a8 100644 --- a/android/src/main/java/com/bleplx/adapter/ScanResult.java +++ b/android/src/main/java/com/bleplx/adapter/ScanResult.java @@ -12,121 +12,121 @@ */ public class ScanResult { - private String deviceId; - private String deviceName; - private int rssi; - private int mtu; - private boolean isConnectable; - @Nullable - private UUID[] overflowServiceUUIDs; - private AdvertisementData advertisementData; - - public ScanResult(String deviceId, String deviceName, int rssi, int mtu, boolean isConnectable, @Nullable UUID[] overflowServiceUUIDs, AdvertisementData advertisementData) { - this.deviceId = deviceId; - this.deviceName = deviceName; - this.rssi = rssi; - this.mtu = mtu; - this.isConnectable = isConnectable; - this.overflowServiceUUIDs = overflowServiceUUIDs; - this.advertisementData = advertisementData; - } - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getDeviceName() { - return deviceName; - } - - public void setDeviceName(String deviceName) { - this.deviceName = deviceName; - } - - public int getRssi() { - return rssi; - } - - public void setRssi(int rssi) { - this.rssi = rssi; - } - - public int getMtu() { - return mtu; - } - - public void setMtu(int mtu) { - this.mtu = mtu; - } - - public boolean isConnectable() { - return isConnectable; - } - - public void setConnectable(boolean connectable) { - isConnectable = connectable; - } - - public UUID[] getOverflowServiceUUIDs() { - return overflowServiceUUIDs; - } - - public void setOverflowServiceUUIDs(@Nullable UUID[] overflowServiceUUIDs) { - this.overflowServiceUUIDs = overflowServiceUUIDs; - } - - public AdvertisementData getAdvertisementData() { - return advertisementData; - } - - public void setAdvertisementData(AdvertisementData advertisementData) { - this.advertisementData = advertisementData; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ScanResult that = (ScanResult) o; - - if (rssi != that.rssi) return false; - if (mtu != that.mtu) return false; - if (isConnectable != that.isConnectable) return false; - if (!deviceId.equals(that.deviceId)) return false; - if (!Objects.equals(deviceName, that.deviceName)) - return false; - // Probably incorrect - comparing Object[] arrays with Arrays.equals - if (!Arrays.equals(overflowServiceUUIDs, that.overflowServiceUUIDs)) return false; - return Objects.equals(advertisementData, that.advertisementData); - } - - @Override - public int hashCode() { - int result = deviceId.hashCode(); - result = 31 * result + (deviceName != null ? deviceName.hashCode() : 0); - result = 31 * result + rssi; - result = 31 * result + mtu; - result = 31 * result + (isConnectable ? 1 : 0); - result = 31 * result + Arrays.hashCode(overflowServiceUUIDs); - result = 31 * result + (advertisementData != null ? advertisementData.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "ScanResult{" + - "deviceId='" + deviceId + '\'' + - ", deviceName='" + deviceName + '\'' + - ", rssi=" + rssi + - ", mtu=" + mtu + - ", isConnectable=" + isConnectable + - ", overflowServiceUUIDs=" + Arrays.toString(overflowServiceUUIDs) + - ", advertisementData=" + advertisementData + - '}'; - } + private String deviceId; + private String deviceName; + private int rssi; + private int mtu; + private boolean isConnectable; + @Nullable + private UUID[] overflowServiceUUIDs; + private AdvertisementData advertisementData; + + public ScanResult(String deviceId, String deviceName, int rssi, int mtu, boolean isConnectable, @Nullable UUID[] overflowServiceUUIDs, AdvertisementData advertisementData) { + this.deviceId = deviceId; + this.deviceName = deviceName; + this.rssi = rssi; + this.mtu = mtu; + this.isConnectable = isConnectable; + this.overflowServiceUUIDs = overflowServiceUUIDs; + this.advertisementData = advertisementData; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public int getRssi() { + return rssi; + } + + public void setRssi(int rssi) { + this.rssi = rssi; + } + + public int getMtu() { + return mtu; + } + + public void setMtu(int mtu) { + this.mtu = mtu; + } + + public boolean isConnectable() { + return isConnectable; + } + + public void setConnectable(boolean connectable) { + isConnectable = connectable; + } + + public UUID[] getOverflowServiceUUIDs() { + return overflowServiceUUIDs; + } + + public void setOverflowServiceUUIDs(@Nullable UUID[] overflowServiceUUIDs) { + this.overflowServiceUUIDs = overflowServiceUUIDs; + } + + public AdvertisementData getAdvertisementData() { + return advertisementData; + } + + public void setAdvertisementData(AdvertisementData advertisementData) { + this.advertisementData = advertisementData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ScanResult that = (ScanResult) o; + + if (rssi != that.rssi) return false; + if (mtu != that.mtu) return false; + if (isConnectable != that.isConnectable) return false; + if (!deviceId.equals(that.deviceId)) return false; + if (!Objects.equals(deviceName, that.deviceName)) + return false; + // Probably incorrect - comparing Object[] arrays with Arrays.equals + if (!Arrays.equals(overflowServiceUUIDs, that.overflowServiceUUIDs)) return false; + return Objects.equals(advertisementData, that.advertisementData); + } + + @Override + public int hashCode() { + int result = deviceId.hashCode(); + result = 31 * result + (deviceName != null ? deviceName.hashCode() : 0); + result = 31 * result + rssi; + result = 31 * result + mtu; + result = 31 * result + (isConnectable ? 1 : 0); + result = 31 * result + Arrays.hashCode(overflowServiceUUIDs); + result = 31 * result + (advertisementData != null ? advertisementData.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ScanResult{" + + "deviceId='" + deviceId + '\'' + + ", deviceName='" + deviceName + '\'' + + ", rssi=" + rssi + + ", mtu=" + mtu + + ", isConnectable=" + isConnectable + + ", overflowServiceUUIDs=" + Arrays.toString(overflowServiceUUIDs) + + ", advertisementData=" + advertisementData + + '}'; + } } diff --git a/android/src/main/java/com/bleplx/adapter/Service.java b/android/src/main/java/com/bleplx/adapter/Service.java index 68ff349d..b23eaa2b 100755 --- a/android/src/main/java/com/bleplx/adapter/Service.java +++ b/android/src/main/java/com/bleplx/adapter/Service.java @@ -10,47 +10,49 @@ import java.util.List; import java.util.UUID; -/** @noinspection unused*/ +/** + * @noinspection unused + */ public class Service { - final private int id; - final private String deviceID; - final private BluetoothGattService btGattService; - - public Service(int id, String deviceID, BluetoothGattService btGattService) { - this.id = id; - this.deviceID = deviceID; - this.btGattService = btGattService; - } - - public int getId() { - return this.id; - } - - public UUID getUuid() { - return btGattService.getUuid(); - } - - public String getDeviceID() { - return deviceID; - } - - public boolean isPrimary() { - return btGattService.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY; - } - - @Nullable - public Characteristic getCharacteristicByUUID(@NonNull UUID uuid) { - BluetoothGattCharacteristic characteristic = btGattService.getCharacteristic(uuid); - if (characteristic == null) return null; - return new Characteristic(this, characteristic); - } - - public List getCharacteristics() { - ArrayList characteristics = new ArrayList<>(btGattService.getCharacteristics().size()); - for (BluetoothGattCharacteristic gattCharacteristic : btGattService.getCharacteristics()) { - characteristics.add(new Characteristic(this, gattCharacteristic)); - } - return characteristics; + final private int id; + final private String deviceID; + final private BluetoothGattService btGattService; + + public Service(int id, String deviceID, BluetoothGattService btGattService) { + this.id = id; + this.deviceID = deviceID; + this.btGattService = btGattService; + } + + public int getId() { + return this.id; + } + + public UUID getUuid() { + return btGattService.getUuid(); + } + + public String getDeviceID() { + return deviceID; + } + + public boolean isPrimary() { + return btGattService.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY; + } + + @Nullable + public Characteristic getCharacteristicByUUID(@NonNull UUID uuid) { + BluetoothGattCharacteristic characteristic = btGattService.getCharacteristic(uuid); + if (characteristic == null) return null; + return new Characteristic(this, characteristic); + } + + public List getCharacteristics() { + ArrayList characteristics = new ArrayList<>(btGattService.getCharacteristics().size()); + for (BluetoothGattCharacteristic gattCharacteristic : btGattService.getCharacteristics()) { + characteristics.add(new Characteristic(this, gattCharacteristic)); } + return characteristics; + } } diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleError.java b/android/src/main/java/com/bleplx/adapter/errors/BleError.java index 89a179a5..802528c8 100755 --- a/android/src/main/java/com/bleplx/adapter/errors/BleError.java +++ b/android/src/main/java/com/bleplx/adapter/errors/BleError.java @@ -3,30 +3,30 @@ public class BleError extends Throwable { - public BleErrorCode errorCode; - public Integer androidCode; - public String reason; - public String deviceID; - public String serviceUUID; - public String characteristicUUID; - public String descriptorUUID; - public String internalMessage; + public BleErrorCode errorCode; + public Integer androidCode; + public String reason; + public String deviceID; + public String serviceUUID; + public String characteristicUUID; + public String descriptorUUID; + public String internalMessage; - public BleError(BleErrorCode errorCode, String reason, Integer androidCode) { - this.errorCode = errorCode; - this.reason = reason; - this.androidCode = androidCode; - } + public BleError(BleErrorCode errorCode, String reason, Integer androidCode) { + this.errorCode = errorCode; + this.reason = reason; + this.androidCode = androidCode; + } - @Override - public String getMessage() { - return "Error code: " + errorCode + - ", android code: " + androidCode + - ", reason" + reason + - ", deviceId" + deviceID + - ", serviceUuid" + serviceUUID + - ", characteristicUuid" + characteristicUUID + - ", descriptorUuid" + descriptorUUID + - ", internalMessage" + internalMessage; - } + @Override + public String getMessage() { + return "Error code: " + errorCode + + ", android code: " + androidCode + + ", reason" + reason + + ", deviceId" + deviceID + + ", serviceUuid" + serviceUUID + + ", characteristicUuid" + characteristicUUID + + ", descriptorUuid" + descriptorUUID + + ", internalMessage" + internalMessage; + } } diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java b/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java index bd17a8fc..fe8a511d 100755 --- a/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java +++ b/android/src/main/java/com/bleplx/adapter/errors/BleErrorCode.java @@ -3,55 +3,55 @@ public enum BleErrorCode { - UnknownError(0), - BluetoothManagerDestroyed(1), - OperationCancelled(2), - OperationTimedOut(3), - OperationStartFailed(4), - InvalidIdentifiers(5), - - BluetoothUnsupported(100), - BluetoothUnauthorized(101), - BluetoothPoweredOff(102), - BluetoothInUnknownState(103), - BluetoothResetting(104), - BluetoothStateChangeFailed(105), - - DeviceConnectionFailed(200), - DeviceDisconnected(201), - DeviceRSSIReadFailed(202), - DeviceAlreadyConnected(203), - DeviceNotFound(204), - DeviceNotConnected(205), - DeviceMTUChangeFailed(206), - - ServicesDiscoveryFailed(300), - IncludedServicesDiscoveryFailed(301), - ServiceNotFound(302), - ServicesNotDiscovered(303), - - CharacteristicsDiscoveryFailed(400), - CharacteristicWriteFailed(401), - CharacteristicReadFailed(402), - CharacteristicNotifyChangeFailed(403), - CharacteristicNotFound(404), - CharacteristicsNotDiscovered(405), - CharacteristicInvalidDataFormat(406), - - DescriptorsDiscoveryFailed(500), - DescriptorWriteFailed(501), - DescriptorReadFailed(502), - DescriptorNotFound(503), - DescriptorsNotDiscovered(504), - DescriptorInvalidDataFormat(505), - DescriptorWriteNotAllowed(506), - - ScanStartFailed(600), - LocationServicesDisabled(601); - - public final int code; - - BleErrorCode(int code) { - this.code = code; - } + UnknownError(0), + BluetoothManagerDestroyed(1), + OperationCancelled(2), + OperationTimedOut(3), + OperationStartFailed(4), + InvalidIdentifiers(5), + + BluetoothUnsupported(100), + BluetoothUnauthorized(101), + BluetoothPoweredOff(102), + BluetoothInUnknownState(103), + BluetoothResetting(104), + BluetoothStateChangeFailed(105), + + DeviceConnectionFailed(200), + DeviceDisconnected(201), + DeviceRSSIReadFailed(202), + DeviceAlreadyConnected(203), + DeviceNotFound(204), + DeviceNotConnected(205), + DeviceMTUChangeFailed(206), + + ServicesDiscoveryFailed(300), + IncludedServicesDiscoveryFailed(301), + ServiceNotFound(302), + ServicesNotDiscovered(303), + + CharacteristicsDiscoveryFailed(400), + CharacteristicWriteFailed(401), + CharacteristicReadFailed(402), + CharacteristicNotifyChangeFailed(403), + CharacteristicNotFound(404), + CharacteristicsNotDiscovered(405), + CharacteristicInvalidDataFormat(406), + + DescriptorsDiscoveryFailed(500), + DescriptorWriteFailed(501), + DescriptorReadFailed(502), + DescriptorNotFound(503), + DescriptorsNotDiscovered(504), + DescriptorInvalidDataFormat(505), + DescriptorWriteNotAllowed(506), + + ScanStartFailed(600), + LocationServicesDisabled(601); + + public final int code; + + BleErrorCode(int code) { + this.code = code; + } } diff --git a/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java b/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java index fed5c0a8..998647c9 100755 --- a/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java +++ b/android/src/main/java/com/bleplx/adapter/errors/BleErrorUtils.java @@ -5,82 +5,82 @@ public class BleErrorUtils { - public static BleError cancelled() { - return new BleError(BleErrorCode.OperationCancelled, null, null); + public static BleError cancelled() { + return new BleError(BleErrorCode.OperationCancelled, null, null); + } + + static public BleError invalidIdentifiers(@NonNull String... identifiers) { + StringBuilder identifiersJoined = new StringBuilder(); + for (String identifier : identifiers) { + identifiersJoined.append(identifier).append(", "); } - static public BleError invalidIdentifiers(@NonNull String... identifiers) { - StringBuilder identifiersJoined = new StringBuilder(); - for (String identifier: identifiers) { - identifiersJoined.append(identifier).append(", "); - } - - BleError bleError = new BleError(BleErrorCode.InvalidIdentifiers, null, null); - bleError.internalMessage = identifiersJoined.toString(); - return bleError; - } - - static public BleError deviceNotFound(String uuid) { - BleError bleError = new BleError(BleErrorCode.DeviceNotFound, null, null); - bleError.deviceID = uuid; - return bleError; - } - - static public BleError deviceNotConnected(String uuid) { - BleError bleError = new BleError(BleErrorCode.DeviceNotConnected, null, null); - bleError.deviceID = uuid; - return bleError; - } - - static public BleError characteristicNotFound(String uuid) { - BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, null, null); - bleError.characteristicUUID = uuid; - return bleError; - } - - static public BleError invalidWriteDataForCharacteristic(String data, String uuid) { - BleError bleError = new BleError(BleErrorCode.CharacteristicInvalidDataFormat, null, null); - bleError.characteristicUUID = uuid; - bleError.internalMessage = data; - return bleError; - } - - static public BleError descriptorNotFound(String uuid) { - BleError bleError = new BleError(BleErrorCode.DescriptorNotFound, null, null); - bleError.descriptorUUID = uuid; - return bleError; - } - - static public BleError invalidWriteDataForDescriptor(String data, String uuid) { - BleError bleError = new BleError(BleErrorCode.DescriptorInvalidDataFormat, null, null); - bleError.descriptorUUID = uuid; - bleError.internalMessage = data; - return bleError; - } - - static public BleError descriptorWriteNotAllowed(String uuid) { - BleError bleError = new BleError(BleErrorCode.DescriptorWriteNotAllowed, null, null); - bleError.descriptorUUID = uuid; - return bleError; - } - - static public BleError serviceNotFound(String uuid) { - BleError bleError = new BleError(BleErrorCode.ServiceNotFound, null, null); - bleError.serviceUUID = uuid; - return bleError; - } - - static public BleError cannotMonitorCharacteristic(String reason, String deviceID, String serviceUUID, String characteristicUUID) { - BleError bleError = new BleError(BleErrorCode.CharacteristicNotifyChangeFailed, reason, null); - bleError.deviceID = deviceID; - bleError.serviceUUID = serviceUUID; - bleError.characteristicUUID = characteristicUUID; - return bleError; - } - - public static BleError deviceServicesNotDiscovered(String deviceID) { - BleError bleError = new BleError(BleErrorCode.ServicesNotDiscovered, null, null); - bleError.deviceID = deviceID; - return bleError; - } + BleError bleError = new BleError(BleErrorCode.InvalidIdentifiers, null, null); + bleError.internalMessage = identifiersJoined.toString(); + return bleError; + } + + static public BleError deviceNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.DeviceNotFound, null, null); + bleError.deviceID = uuid; + return bleError; + } + + static public BleError deviceNotConnected(String uuid) { + BleError bleError = new BleError(BleErrorCode.DeviceNotConnected, null, null); + bleError.deviceID = uuid; + return bleError; + } + + static public BleError characteristicNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, null, null); + bleError.characteristicUUID = uuid; + return bleError; + } + + static public BleError invalidWriteDataForCharacteristic(String data, String uuid) { + BleError bleError = new BleError(BleErrorCode.CharacteristicInvalidDataFormat, null, null); + bleError.characteristicUUID = uuid; + bleError.internalMessage = data; + return bleError; + } + + static public BleError descriptorNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorNotFound, null, null); + bleError.descriptorUUID = uuid; + return bleError; + } + + static public BleError invalidWriteDataForDescriptor(String data, String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorInvalidDataFormat, null, null); + bleError.descriptorUUID = uuid; + bleError.internalMessage = data; + return bleError; + } + + static public BleError descriptorWriteNotAllowed(String uuid) { + BleError bleError = new BleError(BleErrorCode.DescriptorWriteNotAllowed, null, null); + bleError.descriptorUUID = uuid; + return bleError; + } + + static public BleError serviceNotFound(String uuid) { + BleError bleError = new BleError(BleErrorCode.ServiceNotFound, null, null); + bleError.serviceUUID = uuid; + return bleError; + } + + static public BleError cannotMonitorCharacteristic(String reason, String deviceID, String serviceUUID, String characteristicUUID) { + BleError bleError = new BleError(BleErrorCode.CharacteristicNotifyChangeFailed, reason, null); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } + + public static BleError deviceServicesNotDiscovered(String deviceID) { + BleError bleError = new BleError(BleErrorCode.ServicesNotDiscovered, null, null); + bleError.deviceID = deviceID; + return bleError; + } } diff --git a/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java b/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java index df3c4b6f..96bb3c66 100755 --- a/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java +++ b/android/src/main/java/com/bleplx/adapter/errors/ErrorConverter.java @@ -25,195 +25,195 @@ public class ErrorConverter { - public BleError toError(Throwable throwable) { - - // Custom exceptions ----------------------------------------------------------------------- - - if (throwable instanceof CannotMonitorCharacteristicException) { - CannotMonitorCharacteristicException exception = (CannotMonitorCharacteristicException) throwable; - Characteristic characteristic = exception.getCharacteristic(); - // TODO: Missing deviceID - return BleErrorUtils.cannotMonitorCharacteristic( - throwable.getMessage(), - null, - UUIDConverter.fromUUID(characteristic.getServiceUUID()), - UUIDConverter.fromUUID(characteristic.getUuid())); - } - - // RxSwift exceptions ---------------------------------------------------------------------- - - if (throwable instanceof TimeoutException) { - return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), null); - } - - // RxAndroidBle exceptions ----------------------------------------------------------------- - - if (throwable instanceof BleAlreadyConnectedException) { - // TODO: Missing deviceID - return new BleError(BleErrorCode.DeviceAlreadyConnected, throwable.getMessage(), null); - } - - if (throwable instanceof BleCannotSetCharacteristicNotificationException) { - BluetoothGattCharacteristic gattCharacteristic = ((BleCannotSetCharacteristicNotificationException) throwable).getBluetoothGattCharacteristic(); - BluetoothGattService gattService = gattCharacteristic.getService(); - // TODO: Missing deviceID - return BleErrorUtils.cannotMonitorCharacteristic( - throwable.getMessage(), - null, - UUIDConverter.fromUUID(gattService.getUuid()), - UUIDConverter.fromUUID(gattCharacteristic.getUuid())); - } - - - if (throwable instanceof BleCharacteristicNotFoundException) { - UUID uuid = ((BleCharacteristicNotFoundException) throwable).getCharacteristicUUID(); - BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, throwable.getMessage(), null); - bleError.characteristicUUID = UUIDConverter.fromUUID(uuid); - return bleError; - } - - if (throwable instanceof BleConflictingNotificationAlreadySetException) { - UUID characteristicUUID = ((BleConflictingNotificationAlreadySetException) throwable).getCharacteristicUuid(); - // TODO: Missing Service UUID and device ID - return BleErrorUtils.cannotMonitorCharacteristic(throwable.getMessage(), null, null, UUIDConverter.fromUUID(characteristicUUID)); - } - - if (throwable instanceof BleDisconnectedException) { - BleDisconnectedException bleDisconnectedException = (BleDisconnectedException) throwable; - BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, throwable.getMessage(), bleDisconnectedException.state); - bleError.deviceID = bleDisconnectedException.bluetoothDeviceAddress; - return bleError; - } - - if (throwable instanceof BleScanException) { - return toError((BleScanException) throwable); - } - - if (throwable instanceof BleServiceNotFoundException) { - BleError bleError = new BleError(BleErrorCode.ServiceNotFound, throwable.getMessage(), null); - bleError.serviceUUID = UUIDConverter.fromUUID(((BleServiceNotFoundException) throwable).getServiceUUID()); - return bleError; - } - - // RxAndroidBle (GATT) exceptions ---------------------------------------------------------- - - if (throwable instanceof BleGattCallbackTimeoutException) { - return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), ((BleGattCallbackTimeoutException) throwable).getStatus()); - } - - if (throwable instanceof BleGattCannotStartException) { - return new BleError(BleErrorCode.OperationStartFailed, throwable.getMessage(), ((BleGattCannotStartException) throwable).getStatus()); - } - - if (throwable instanceof BleGattCharacteristicException) { - BleGattCharacteristicException exception = (BleGattCharacteristicException) throwable; - int code = exception.getStatus(); - BleGattOperationType operationType = exception.getBleGattOperationType(); - - return toError(code, - throwable.getMessage(), - operationType, - exception.getMacAddress(), - UUIDConverter.fromUUID(exception.characteristic.getService().getUuid()), - UUIDConverter.fromUUID(exception.characteristic.getUuid()), - null); - } - - if (throwable instanceof BleGattDescriptorException) { - BleGattDescriptorException exception = (BleGattDescriptorException) throwable; - int code = exception.getStatus(); - BleGattOperationType operationType = exception.getBleGattOperationType(); - - return toError(code, - throwable.getMessage(), - operationType, - exception.getMacAddress(), - UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getService().getUuid()), - UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getUuid()), - UUIDConverter.fromUUID(exception.descriptor.getUuid())); - } - - if (throwable instanceof BleGattException) { - BleGattException exception = (BleGattException) throwable; - int code = exception.getStatus(); - BleGattOperationType operationType = exception.getBleGattOperationType(); - - return toError(code, - throwable.getMessage(), - operationType, - exception.getMacAddress(), - null, - null, - null); - } - - return new BleError(BleErrorCode.UnknownError, throwable.toString(), null); - } - - private BleError toError(int code, String message, BleGattOperationType operationType, String deviceID, String serviceUUID, String characteristicUUID, String descriptorUUID) { - if (BleGattOperationType.CONNECTION_STATE == operationType) { - BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, message, code); - bleError.deviceID = deviceID; - return bleError; - } else if (BleGattOperationType.SERVICE_DISCOVERY == operationType) { - BleError bleError = new BleError(BleErrorCode.ServicesDiscoveryFailed, message, code); - bleError.deviceID = deviceID; - return bleError; - } else if (BleGattOperationType.CHARACTERISTIC_READ == operationType || BleGattOperationType.CHARACTERISTIC_CHANGED == operationType) { - BleError bleError = new BleError(BleErrorCode.CharacteristicReadFailed, message, code); - bleError.deviceID = deviceID; - bleError.serviceUUID = serviceUUID; - bleError.characteristicUUID = characteristicUUID; - return bleError; - } else if (BleGattOperationType.CHARACTERISTIC_WRITE == operationType || BleGattOperationType.CHARACTERISTIC_LONG_WRITE == operationType || BleGattOperationType.RELIABLE_WRITE_COMPLETED == operationType) { - BleError bleError = new BleError(BleErrorCode.CharacteristicWriteFailed, message, code); - bleError.deviceID = deviceID; - bleError.serviceUUID = serviceUUID; - bleError.characteristicUUID = characteristicUUID; - return bleError; - } else if (BleGattOperationType.DESCRIPTOR_READ == operationType) { - BleError bleError = new BleError(BleErrorCode.DescriptorReadFailed, message, code); - bleError.deviceID = deviceID; - bleError.serviceUUID = serviceUUID; - bleError.characteristicUUID = characteristicUUID; - bleError.descriptorUUID = descriptorUUID; - return bleError; - } else if (BleGattOperationType.DESCRIPTOR_WRITE == operationType) { - BleError bleError = new BleError(BleErrorCode.DescriptorWriteFailed, message, code); - bleError.deviceID = deviceID; - bleError.serviceUUID = serviceUUID; - bleError.characteristicUUID = characteristicUUID; - bleError.descriptorUUID = descriptorUUID; - return bleError; - } else if (BleGattOperationType.READ_RSSI == operationType) { - BleError bleError = new BleError(BleErrorCode.DeviceRSSIReadFailed, message, code); - bleError.deviceID = deviceID; - return bleError; - } else if (BleGattOperationType.ON_MTU_CHANGED == operationType) { - BleError bleError = new BleError(BleErrorCode.DeviceMTUChangeFailed, message, code); - bleError.deviceID = deviceID; - return bleError; - } else if (BleGattOperationType.CONNECTION_PRIORITY_CHANGE == operationType) { - // TODO: Handle? - } - - return new BleError(BleErrorCode.UnknownError, message, code); - } - - private BleError toError(BleScanException bleScanException) { - final int reason = bleScanException.getReason(); - switch (reason) { - case BleScanException.BLUETOOTH_DISABLED: - return new BleError(BleErrorCode.BluetoothPoweredOff, bleScanException.getMessage(), null); - case BleScanException.BLUETOOTH_NOT_AVAILABLE: - return new BleError(BleErrorCode.BluetoothUnsupported, bleScanException.getMessage(), null); - case BleScanException.LOCATION_PERMISSION_MISSING: - return new BleError(BleErrorCode.BluetoothUnauthorized, bleScanException.getMessage(), null); - case BleScanException.LOCATION_SERVICES_DISABLED: - return new BleError(BleErrorCode.LocationServicesDisabled, bleScanException.getMessage(), null); - case BleScanException.BLUETOOTH_CANNOT_START: - default: - return new BleError(BleErrorCode.ScanStartFailed, bleScanException.getMessage(), null); - } + public BleError toError(Throwable throwable) { + + // Custom exceptions ----------------------------------------------------------------------- + + if (throwable instanceof CannotMonitorCharacteristicException) { + CannotMonitorCharacteristicException exception = (CannotMonitorCharacteristicException) throwable; + Characteristic characteristic = exception.getCharacteristic(); + // TODO: Missing deviceID + return BleErrorUtils.cannotMonitorCharacteristic( + throwable.getMessage(), + null, + UUIDConverter.fromUUID(characteristic.getServiceUUID()), + UUIDConverter.fromUUID(characteristic.getUuid())); } + + // RxSwift exceptions ---------------------------------------------------------------------- + + if (throwable instanceof TimeoutException) { + return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), null); + } + + // RxAndroidBle exceptions ----------------------------------------------------------------- + + if (throwable instanceof BleAlreadyConnectedException) { + // TODO: Missing deviceID + return new BleError(BleErrorCode.DeviceAlreadyConnected, throwable.getMessage(), null); + } + + if (throwable instanceof BleCannotSetCharacteristicNotificationException) { + BluetoothGattCharacteristic gattCharacteristic = ((BleCannotSetCharacteristicNotificationException) throwable).getBluetoothGattCharacteristic(); + BluetoothGattService gattService = gattCharacteristic.getService(); + // TODO: Missing deviceID + return BleErrorUtils.cannotMonitorCharacteristic( + throwable.getMessage(), + null, + UUIDConverter.fromUUID(gattService.getUuid()), + UUIDConverter.fromUUID(gattCharacteristic.getUuid())); + } + + + if (throwable instanceof BleCharacteristicNotFoundException) { + UUID uuid = ((BleCharacteristicNotFoundException) throwable).getCharacteristicUUID(); + BleError bleError = new BleError(BleErrorCode.CharacteristicNotFound, throwable.getMessage(), null); + bleError.characteristicUUID = UUIDConverter.fromUUID(uuid); + return bleError; + } + + if (throwable instanceof BleConflictingNotificationAlreadySetException) { + UUID characteristicUUID = ((BleConflictingNotificationAlreadySetException) throwable).getCharacteristicUuid(); + // TODO: Missing Service UUID and device ID + return BleErrorUtils.cannotMonitorCharacteristic(throwable.getMessage(), null, null, UUIDConverter.fromUUID(characteristicUUID)); + } + + if (throwable instanceof BleDisconnectedException) { + BleDisconnectedException bleDisconnectedException = (BleDisconnectedException) throwable; + BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, throwable.getMessage(), bleDisconnectedException.state); + bleError.deviceID = bleDisconnectedException.bluetoothDeviceAddress; + return bleError; + } + + if (throwable instanceof BleScanException) { + return toError((BleScanException) throwable); + } + + if (throwable instanceof BleServiceNotFoundException) { + BleError bleError = new BleError(BleErrorCode.ServiceNotFound, throwable.getMessage(), null); + bleError.serviceUUID = UUIDConverter.fromUUID(((BleServiceNotFoundException) throwable).getServiceUUID()); + return bleError; + } + + // RxAndroidBle (GATT) exceptions ---------------------------------------------------------- + + if (throwable instanceof BleGattCallbackTimeoutException) { + return new BleError(BleErrorCode.OperationTimedOut, throwable.getMessage(), ((BleGattCallbackTimeoutException) throwable).getStatus()); + } + + if (throwable instanceof BleGattCannotStartException) { + return new BleError(BleErrorCode.OperationStartFailed, throwable.getMessage(), ((BleGattCannotStartException) throwable).getStatus()); + } + + if (throwable instanceof BleGattCharacteristicException) { + BleGattCharacteristicException exception = (BleGattCharacteristicException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + UUIDConverter.fromUUID(exception.characteristic.getService().getUuid()), + UUIDConverter.fromUUID(exception.characteristic.getUuid()), + null); + } + + if (throwable instanceof BleGattDescriptorException) { + BleGattDescriptorException exception = (BleGattDescriptorException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getService().getUuid()), + UUIDConverter.fromUUID(exception.descriptor.getCharacteristic().getUuid()), + UUIDConverter.fromUUID(exception.descriptor.getUuid())); + } + + if (throwable instanceof BleGattException) { + BleGattException exception = (BleGattException) throwable; + int code = exception.getStatus(); + BleGattOperationType operationType = exception.getBleGattOperationType(); + + return toError(code, + throwable.getMessage(), + operationType, + exception.getMacAddress(), + null, + null, + null); + } + + return new BleError(BleErrorCode.UnknownError, throwable.toString(), null); + } + + private BleError toError(int code, String message, BleGattOperationType operationType, String deviceID, String serviceUUID, String characteristicUUID, String descriptorUUID) { + if (BleGattOperationType.CONNECTION_STATE == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceDisconnected, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.SERVICE_DISCOVERY == operationType) { + BleError bleError = new BleError(BleErrorCode.ServicesDiscoveryFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.CHARACTERISTIC_READ == operationType || BleGattOperationType.CHARACTERISTIC_CHANGED == operationType) { + BleError bleError = new BleError(BleErrorCode.CharacteristicReadFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } else if (BleGattOperationType.CHARACTERISTIC_WRITE == operationType || BleGattOperationType.CHARACTERISTIC_LONG_WRITE == operationType || BleGattOperationType.RELIABLE_WRITE_COMPLETED == operationType) { + BleError bleError = new BleError(BleErrorCode.CharacteristicWriteFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + return bleError; + } else if (BleGattOperationType.DESCRIPTOR_READ == operationType) { + BleError bleError = new BleError(BleErrorCode.DescriptorReadFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + bleError.descriptorUUID = descriptorUUID; + return bleError; + } else if (BleGattOperationType.DESCRIPTOR_WRITE == operationType) { + BleError bleError = new BleError(BleErrorCode.DescriptorWriteFailed, message, code); + bleError.deviceID = deviceID; + bleError.serviceUUID = serviceUUID; + bleError.characteristicUUID = characteristicUUID; + bleError.descriptorUUID = descriptorUUID; + return bleError; + } else if (BleGattOperationType.READ_RSSI == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceRSSIReadFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.ON_MTU_CHANGED == operationType) { + BleError bleError = new BleError(BleErrorCode.DeviceMTUChangeFailed, message, code); + bleError.deviceID = deviceID; + return bleError; + } else if (BleGattOperationType.CONNECTION_PRIORITY_CHANGE == operationType) { + // TODO: Handle? + } + + return new BleError(BleErrorCode.UnknownError, message, code); + } + + private BleError toError(BleScanException bleScanException) { + final int reason = bleScanException.getReason(); + switch (reason) { + case BleScanException.BLUETOOTH_DISABLED: + return new BleError(BleErrorCode.BluetoothPoweredOff, bleScanException.getMessage(), null); + case BleScanException.BLUETOOTH_NOT_AVAILABLE: + return new BleError(BleErrorCode.BluetoothUnsupported, bleScanException.getMessage(), null); + case BleScanException.LOCATION_PERMISSION_MISSING: + return new BleError(BleErrorCode.BluetoothUnauthorized, bleScanException.getMessage(), null); + case BleScanException.LOCATION_SERVICES_DISABLED: + return new BleError(BleErrorCode.LocationServicesDisabled, bleScanException.getMessage(), null); + case BleScanException.BLUETOOTH_CANNOT_START: + default: + return new BleError(BleErrorCode.ScanStartFailed, bleScanException.getMessage(), null); + } + } } diff --git a/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java b/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java index a76d06cb..bc94b7f8 100755 --- a/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java +++ b/android/src/main/java/com/bleplx/adapter/exceptions/CannotMonitorCharacteristicException.java @@ -3,13 +3,13 @@ import com.bleplx.adapter.Characteristic; public class CannotMonitorCharacteristicException extends RuntimeException { - private Characteristic characteristic; + private Characteristic characteristic; - public CannotMonitorCharacteristicException(Characteristic characteristic) { - this.characteristic = characteristic; - } + public CannotMonitorCharacteristicException(Characteristic characteristic) { + this.characteristic = characteristic; + } - public Characteristic getCharacteristic() { - return characteristic; - } + public Characteristic getCharacteristic() { + return characteristic; + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java b/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java index 0acb5343..814fe773 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java +++ b/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java @@ -3,10 +3,11 @@ import android.util.Base64; public class Base64Converter { - public static String encode(byte[] bytes) { - return Base64.encodeToString(bytes, Base64.NO_WRAP); - } - public static byte[] decode(String base64) { - return Base64.decode(base64, Base64.NO_WRAP); - } + public static String encode(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.NO_WRAP); + } + + public static byte[] decode(String base64) { + return Base64.decode(base64, Base64.NO_WRAP); + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java b/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java index 86bcbea4..111ec29a 100644 --- a/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java +++ b/android/src/main/java/com/bleplx/adapter/utils/ByteUtils.java @@ -1,15 +1,15 @@ package com.bleplx.adapter.utils; public class ByteUtils { - private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); + private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for ( int j = 0; j < bytes.length; j++ ) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } + return new String(hexChars); + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/Constants.java b/android/src/main/java/com/bleplx/adapter/utils/Constants.java index 20fec4fe..048cefe9 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/Constants.java +++ b/android/src/main/java/com/bleplx/adapter/utils/Constants.java @@ -10,57 +10,57 @@ public interface Constants { - @StringDef({ - BluetoothState.UNKNOWN, - BluetoothState.RESETTING, - BluetoothState.UNSUPPORTED, - BluetoothState.UNAUTHORIZED, - BluetoothState.POWERED_OFF, - BluetoothState.POWERED_ON} - ) - @Retention(RetentionPolicy.SOURCE) - @interface BluetoothState { + @StringDef({ + BluetoothState.UNKNOWN, + BluetoothState.RESETTING, + BluetoothState.UNSUPPORTED, + BluetoothState.UNAUTHORIZED, + BluetoothState.POWERED_OFF, + BluetoothState.POWERED_ON} + ) + @Retention(RetentionPolicy.SOURCE) + @interface BluetoothState { - String UNKNOWN = "Unknown"; - String RESETTING = "Resetting"; - String UNSUPPORTED = "Unsupported"; - String UNAUTHORIZED = "Unauthorized"; - String POWERED_OFF = "PoweredOff"; - String POWERED_ON = "PoweredOn"; - } + String UNKNOWN = "Unknown"; + String RESETTING = "Resetting"; + String UNSUPPORTED = "Unsupported"; + String UNAUTHORIZED = "Unauthorized"; + String POWERED_OFF = "PoweredOff"; + String POWERED_ON = "PoweredOn"; + } - @StringDef({ - BluetoothLogLevel.NONE, - BluetoothLogLevel.VERBOSE, - BluetoothLogLevel.DEBUG, - BluetoothLogLevel.INFO, - BluetoothLogLevel.WARNING, - BluetoothLogLevel.ERROR} - ) - @Retention(RetentionPolicy.SOURCE) - @interface BluetoothLogLevel { + @StringDef({ + BluetoothLogLevel.NONE, + BluetoothLogLevel.VERBOSE, + BluetoothLogLevel.DEBUG, + BluetoothLogLevel.INFO, + BluetoothLogLevel.WARNING, + BluetoothLogLevel.ERROR} + ) + @Retention(RetentionPolicy.SOURCE) + @interface BluetoothLogLevel { - String NONE = "None"; - String VERBOSE = "Verbose"; - String DEBUG = "Debug"; - String INFO = "Info"; - String WARNING = "Warning"; - String ERROR = "Error"; - } + String NONE = "None"; + String VERBOSE = "Verbose"; + String DEBUG = "Debug"; + String INFO = "Info"; + String WARNING = "Warning"; + String ERROR = "Error"; + } - @IntDef({ - ConnectionPriority.BALANCED, - ConnectionPriority.HIGH, - ConnectionPriority.LOW_POWER} - ) - @Retention(RetentionPolicy.SOURCE) - @interface ConnectionPriority { + @IntDef({ + ConnectionPriority.BALANCED, + ConnectionPriority.HIGH, + ConnectionPriority.LOW_POWER} + ) + @Retention(RetentionPolicy.SOURCE) + @interface ConnectionPriority { - int BALANCED = 0; - int HIGH = 1; - int LOW_POWER = 2; - } + int BALANCED = 0; + int HIGH = 1; + int LOW_POWER = 2; + } - int MINIMUM_MTU = 23; - UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + int MINIMUM_MTU = 23; + UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); } diff --git a/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java b/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java index 94f20ac5..83e94729 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java +++ b/android/src/main/java/com/bleplx/adapter/utils/DisposableMap.java @@ -8,32 +8,32 @@ public class DisposableMap { - final private Map subscriptions = new HashMap<>(); + final private Map subscriptions = new HashMap<>(); - public synchronized void replaceSubscription(String key, Disposable subscription) { - Disposable oldSubscription = subscriptions.put(key, subscription); - if (oldSubscription != null && !oldSubscription.isDisposed()) { - oldSubscription.dispose(); - } + public synchronized void replaceSubscription(String key, Disposable subscription) { + Disposable oldSubscription = subscriptions.put(key, subscription); + if (oldSubscription != null && !oldSubscription.isDisposed()) { + oldSubscription.dispose(); } + } - public synchronized boolean removeSubscription(String key) { - Disposable subscription = subscriptions.remove(key); - if (subscription == null) return false; - if (!subscription.isDisposed()) { - subscription.dispose(); - } - return true; + public synchronized boolean removeSubscription(String key) { + Disposable subscription = subscriptions.remove(key); + if (subscription == null) return false; + if (!subscription.isDisposed()) { + subscription.dispose(); } + return true; + } - public synchronized void removeAllSubscriptions() { - Iterator> it = subscriptions.entrySet().iterator(); - while (it.hasNext()) { - Disposable subscription = it.next().getValue(); - it.remove(); - if (!subscription.isDisposed()) { - subscription.dispose(); - } - } + public synchronized void removeAllSubscriptions() { + Iterator> it = subscriptions.entrySet().iterator(); + while (it.hasNext()) { + Disposable subscription = it.next().getValue(); + it.remove(); + if (!subscription.isDisposed()) { + subscription.dispose(); + } } + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java b/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java index 94305d3b..116124e1 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java +++ b/android/src/main/java/com/bleplx/adapter/utils/IdGenerator.java @@ -3,20 +3,20 @@ import java.util.HashMap; public class IdGenerator { - private static HashMap idMap = new HashMap<>(); - private static int nextKey = 0; + private static HashMap idMap = new HashMap<>(); + private static int nextKey = 0; - public static int getIdForKey(IdGeneratorKey idGeneratorKey) { - Integer id = idMap.get(idGeneratorKey); - if (id != null) { - return id; - } - idMap.put(idGeneratorKey, ++nextKey); - return nextKey; + public static int getIdForKey(IdGeneratorKey idGeneratorKey) { + Integer id = idMap.get(idGeneratorKey); + if (id != null) { + return id; } + idMap.put(idGeneratorKey, ++nextKey); + return nextKey; + } - public static void clear() { - idMap.clear(); - nextKey = 0; - } + public static void clear() { + idMap.clear(); + nextKey = 0; + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java b/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java index ca193cd8..721b9f6f 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java +++ b/android/src/main/java/com/bleplx/adapter/utils/IdGeneratorKey.java @@ -5,33 +5,33 @@ public class IdGeneratorKey { - private final String deviceAddress; - private final UUID uuid; - private final int id; - - public IdGeneratorKey(String deviceAddress, UUID uuid, int id) { - this.deviceAddress = deviceAddress; - this.uuid = uuid; - this.id = id; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - IdGeneratorKey that = (IdGeneratorKey) o; - - if (id != that.id) return false; - if (!deviceAddress.equals(that.deviceAddress)) return false; - return uuid.equals(that.uuid); - } - - @Override - public int hashCode() { - int result = deviceAddress.hashCode(); - result = 31 * result + uuid.hashCode(); - result = 31 * result + id; - return result; - } + private final String deviceAddress; + private final UUID uuid; + private final int id; + + public IdGeneratorKey(String deviceAddress, UUID uuid, int id) { + this.deviceAddress = deviceAddress; + this.uuid = uuid; + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IdGeneratorKey that = (IdGeneratorKey) o; + + if (id != that.id) return false; + if (!deviceAddress.equals(that.deviceAddress)) return false; + return uuid.equals(that.uuid); + } + + @Override + public int hashCode() { + int result = deviceAddress.hashCode(); + result = 31 * result + uuid.hashCode(); + result = 31 * result + id; + return result; + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java b/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java index 11f8628e..eb249fd1 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java +++ b/android/src/main/java/com/bleplx/adapter/utils/LogLevel.java @@ -5,43 +5,43 @@ public class LogLevel { - @RxBleLog.LogLevel - public static int toLogLevel(String logLevel) { - switch (logLevel) { - case Constants.BluetoothLogLevel.VERBOSE: - return RxBleLog.VERBOSE; - case Constants.BluetoothLogLevel.DEBUG: - return RxBleLog.DEBUG; - case Constants.BluetoothLogLevel.INFO: - return RxBleLog.INFO; - case Constants.BluetoothLogLevel.WARNING: - return RxBleLog.WARN; - case Constants.BluetoothLogLevel.ERROR: - return RxBleLog.ERROR; - case Constants.BluetoothLogLevel.NONE: - // fallthrough - default: - return RxBleLog.NONE; - } + @RxBleLog.LogLevel + public static int toLogLevel(String logLevel) { + switch (logLevel) { + case Constants.BluetoothLogLevel.VERBOSE: + return RxBleLog.VERBOSE; + case Constants.BluetoothLogLevel.DEBUG: + return RxBleLog.DEBUG; + case Constants.BluetoothLogLevel.INFO: + return RxBleLog.INFO; + case Constants.BluetoothLogLevel.WARNING: + return RxBleLog.WARN; + case Constants.BluetoothLogLevel.ERROR: + return RxBleLog.ERROR; + case Constants.BluetoothLogLevel.NONE: + // fallthrough + default: + return RxBleLog.NONE; } + } - @Constants.BluetoothLogLevel - public static String fromLogLevel(int logLevel) { - switch (logLevel) { - case RxBleLog.VERBOSE: - return Constants.BluetoothLogLevel.VERBOSE; - case RxBleLog.DEBUG: - return Constants.BluetoothLogLevel.DEBUG; - case RxBleLog.INFO: - return Constants.BluetoothLogLevel.INFO; - case RxBleLog.WARN: - return Constants.BluetoothLogLevel.WARNING; - case RxBleLog.ERROR: - return Constants.BluetoothLogLevel.ERROR; - case RxBleLog.NONE: - // fallthrough - default: - return Constants.BluetoothLogLevel.NONE; - } + @Constants.BluetoothLogLevel + public static String fromLogLevel(int logLevel) { + switch (logLevel) { + case RxBleLog.VERBOSE: + return Constants.BluetoothLogLevel.VERBOSE; + case RxBleLog.DEBUG: + return Constants.BluetoothLogLevel.DEBUG; + case RxBleLog.INFO: + return Constants.BluetoothLogLevel.INFO; + case RxBleLog.WARN: + return Constants.BluetoothLogLevel.WARNING; + case RxBleLog.ERROR: + return Constants.BluetoothLogLevel.ERROR; + case RxBleLog.NONE: + // fallthrough + default: + return Constants.BluetoothLogLevel.NONE; } + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java b/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java index 20b12f18..68f7fff0 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java +++ b/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java @@ -16,33 +16,35 @@ public class RefreshGattCustomOperation implements RxBleCustomOperation { - /** @noinspection unchecked, JavaReflectionMemberAccess, DataFlowIssue */ - @NonNull - @Override - public Observable asObservable( - final BluetoothGatt bluetoothGatt, - final RxBleGattCallback rxBleGattCallback, - final Scheduler scheduler - ) { - return Observable.ambArray( - Observable.fromCallable(() -> { - boolean success = false; - try { - Method bluetoothGattRefreshFunction = bluetoothGatt.getClass().getMethod("refresh"); - - success = (Boolean) bluetoothGattRefreshFunction.invoke(bluetoothGatt); - - if (!success) RxBleLog.d("BluetoothGatt.refresh() returned false"); - } catch (Exception e) { - RxBleLog.d(e, "Could not call function BluetoothGatt.refresh()"); - } - - RxBleLog.i("Calling BluetoothGatt.refresh() status: %s", success ? "Success" : "Failure"); - return success; - }) - .subscribeOn(scheduler) - .delay(1, TimeUnit.SECONDS, scheduler), - rxBleGattCallback.observeDisconnect() - ); - } + /** + * @noinspection unchecked, JavaReflectionMemberAccess, DataFlowIssue + */ + @NonNull + @Override + public Observable asObservable( + final BluetoothGatt bluetoothGatt, + final RxBleGattCallback rxBleGattCallback, + final Scheduler scheduler + ) { + return Observable.ambArray( + Observable.fromCallable(() -> { + boolean success = false; + try { + Method bluetoothGattRefreshFunction = bluetoothGatt.getClass().getMethod("refresh"); + + success = (Boolean) bluetoothGattRefreshFunction.invoke(bluetoothGatt); + + if (!success) RxBleLog.d("BluetoothGatt.refresh() returned false"); + } catch (Exception e) { + RxBleLog.d(e, "Could not call function BluetoothGatt.refresh()"); + } + + RxBleLog.i("Calling BluetoothGatt.refresh() status: %s", success ? "Success" : "Failure"); + return success; + }) + .subscribeOn(scheduler) + .delay(1, TimeUnit.SECONDS, scheduler), + rxBleGattCallback.observeDisconnect() + ); + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java b/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java index c4943fb9..fa19f4e3 100644 --- a/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java +++ b/android/src/main/java/com/bleplx/adapter/utils/SafeExecutor.java @@ -10,24 +10,24 @@ public class SafeExecutor { - private final OnSuccessCallback successCallback; - private final OnErrorCallback errorCallback; - private final AtomicBoolean wasExecuted = new AtomicBoolean(false); - - public SafeExecutor(@Nullable OnSuccessCallback successCallback, @Nullable OnErrorCallback errorCallback) { - this.successCallback = successCallback; - this.errorCallback = errorCallback; - } - - public void success(T data) { - if (wasExecuted.compareAndSet(false, true) && successCallback != null) { - successCallback.onSuccess(data); - } + private final OnSuccessCallback successCallback; + private final OnErrorCallback errorCallback; + private final AtomicBoolean wasExecuted = new AtomicBoolean(false); + + public SafeExecutor(@Nullable OnSuccessCallback successCallback, @Nullable OnErrorCallback errorCallback) { + this.successCallback = successCallback; + this.errorCallback = errorCallback; + } + + public void success(T data) { + if (wasExecuted.compareAndSet(false, true) && successCallback != null) { + successCallback.onSuccess(data); } + } - public void error(BleError error) { - if (wasExecuted.compareAndSet(false, true) && errorCallback != null) { - errorCallback.onError(error); - } + public void error(BleError error) { + if (wasExecuted.compareAndSet(false, true) && errorCallback != null) { + errorCallback.onError(error); } + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java b/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java index cd58e485..b4b2f9f0 100644 --- a/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java +++ b/android/src/main/java/com/bleplx/adapter/utils/ServiceFactory.java @@ -6,11 +6,11 @@ public class ServiceFactory { - public Service create(String deviceId, BluetoothGattService btGattService) { - return new Service( - IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, btGattService.getUuid(), btGattService.getInstanceId())), - deviceId, - btGattService - ); - } + public Service create(String deviceId, BluetoothGattService btGattService) { + return new Service( + IdGenerator.getIdForKey(new IdGeneratorKey(deviceId, btGattService.getUuid(), btGattService.getInstanceId())), + deviceId, + btGattService + ); + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java b/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java index 141172d2..96247f8c 100755 --- a/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java +++ b/android/src/main/java/com/bleplx/adapter/utils/UUIDConverter.java @@ -5,47 +5,47 @@ public class UUIDConverter { - private static String baseUUIDPrefix = "0000"; - private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB"; - - public static UUID convert(String sUUID) { - if (sUUID == null) return null; - - if (sUUID.length() == 4) { - sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; - } else if (sUUID.length() == 8) { - sUUID = sUUID + baseUUIDSuffix; - } - - try { - return UUID.fromString(sUUID); - } catch (Throwable e) { - return null; - } + private static String baseUUIDPrefix = "0000"; + private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB"; + + public static UUID convert(String sUUID) { + if (sUUID == null) return null; + + if (sUUID.length() == 4) { + sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; + } else if (sUUID.length() == 8) { + sUUID = sUUID + baseUUIDSuffix; } - public static UUID[] convert(String... sUUIDs) { - UUID[] UUIDs = new UUID[sUUIDs.length]; - for (int i = 0; i < sUUIDs.length; i++) { - - if (sUUIDs[i] == null) return null; - - if (sUUIDs[i].length() == 4) { - sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix; - } else if (sUUIDs[i].length() == 8) { - sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix; - } - - try { - UUIDs[i] = UUID.fromString(sUUIDs[i]); - } catch (Throwable e) { - return null; - } - } - return UUIDs; + try { + return UUID.fromString(sUUID); + } catch (Throwable e) { + return null; } + } + + public static UUID[] convert(String... sUUIDs) { + UUID[] UUIDs = new UUID[sUUIDs.length]; + for (int i = 0; i < sUUIDs.length; i++) { - public static String fromUUID(UUID uuid) { - return uuid.toString().toLowerCase(); + if (sUUIDs[i] == null) return null; + + if (sUUIDs[i].length() == 4) { + sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix; + } else if (sUUIDs[i].length() == 8) { + sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix; + } + + try { + UUIDs[i] = UUID.fromString(sUUIDs[i]); + } catch (Throwable e) { + return null; + } } + return UUIDs; + } + + public static String fromUUID(UUID uuid) { + return uuid.toString().toLowerCase(); + } } diff --git a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java index 67c42229..8e943e19 100644 --- a/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java +++ b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxBleDeviceToDeviceMapper.java @@ -9,11 +9,11 @@ public class RxBleDeviceToDeviceMapper { - public Device map(@NonNull RxBleDevice rxDevice, @Nullable RxBleConnection connection) { - Device device = new Device(rxDevice.getMacAddress(), rxDevice.getName()); - if (connection != null) { - device.setMtu(connection.getMtu()); - } - return device; + public Device map(@NonNull RxBleDevice rxDevice, @Nullable RxBleConnection connection) { + Device device = new Device(rxDevice.getMacAddress(), rxDevice.getName()); + if (connection != null) { + device.setMtu(connection.getMtu()); } + return device; + } } diff --git a/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java index 22168eb3..370f1352 100644 --- a/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java @@ -1,66 +1,65 @@ package com.bleplx.converter; +import com.bleplx.adapter.errors.BleError; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.bleplx.adapter.errors.BleError; public class BleErrorToJsObjectConverter { - public ReadableArray toJSCallback(BleError error) { - WritableArray array = Arguments.createArray(); - array.pushString(toJs(error)); - array.pushNull(); - return array; - } + public ReadableArray toJSCallback(BleError error) { + WritableArray array = Arguments.createArray(); + array.pushString(toJs(error)); + array.pushNull(); + return array; + } - public String toJs(BleError error) { - WritableArray array = Arguments.createArray(); - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("{"); + public String toJs(BleError error) { + WritableArray array = Arguments.createArray(); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{"); - stringBuilder.append("\"errorCode\":"); - stringBuilder.append(error.errorCode.code); + stringBuilder.append("\"errorCode\":"); + stringBuilder.append(error.errorCode.code); - stringBuilder.append(",\"attErrorCode\":"); - if (error.androidCode == null || error.androidCode >= 0x80 || error.androidCode < 0) { - stringBuilder.append("null"); - } else { - stringBuilder.append(error.androidCode.intValue()); - } + stringBuilder.append(",\"attErrorCode\":"); + if (error.androidCode == null || error.androidCode >= 0x80 || error.androidCode < 0) { + stringBuilder.append("null"); + } else { + stringBuilder.append(error.androidCode.intValue()); + } - stringBuilder.append(",\"iosErrorCode\": null"); + stringBuilder.append(",\"iosErrorCode\": null"); - stringBuilder.append(",\"androidErrorCode\":"); - if (error.androidCode == null || error.androidCode < 0x80) { - stringBuilder.append("null"); - } else { - stringBuilder.append(error.androidCode.intValue()); - } + stringBuilder.append(",\"androidErrorCode\":"); + if (error.androidCode == null || error.androidCode < 0x80) { + stringBuilder.append("null"); + } else { + stringBuilder.append(error.androidCode.intValue()); + } - appendString(stringBuilder, "reason", error.reason); - appendString(stringBuilder, "deviceID", error.deviceID); - appendString(stringBuilder, "serviceUUID", error.serviceUUID); - appendString(stringBuilder, "characteristicUUID", error.characteristicUUID); - appendString(stringBuilder, "descriptorUUID", error.descriptorUUID); - appendString(stringBuilder, "internalMessage", error.internalMessage); + appendString(stringBuilder, "reason", error.reason); + appendString(stringBuilder, "deviceID", error.deviceID); + appendString(stringBuilder, "serviceUUID", error.serviceUUID); + appendString(stringBuilder, "characteristicUUID", error.characteristicUUID); + appendString(stringBuilder, "descriptorUUID", error.descriptorUUID); + appendString(stringBuilder, "internalMessage", error.internalMessage); - stringBuilder.append("}"); + stringBuilder.append("}"); - return stringBuilder.toString(); - } + return stringBuilder.toString(); + } - private void appendString(StringBuilder stringBuilder, String key, String value) { - stringBuilder.append(",\""); - stringBuilder.append(key); - stringBuilder.append("\":"); - if (value == null) { - stringBuilder.append("null"); - } else { - stringBuilder.append("\""); - stringBuilder.append(value); - stringBuilder.append("\""); - } + private void appendString(StringBuilder stringBuilder, String key, String value) { + stringBuilder.append(",\""); + stringBuilder.append(key); + stringBuilder.append("\":"); + if (value == null) { + stringBuilder.append("null"); + } else { + stringBuilder.append("\""); + stringBuilder.append(value); + stringBuilder.append("\""); } + } } diff --git a/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java index 292499bf..270b4f62 100644 --- a/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/CharacteristicToJsObjectConverter.java @@ -1,46 +1,46 @@ package com.bleplx.converter; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; import com.bleplx.adapter.Characteristic; import com.bleplx.adapter.utils.Base64Converter; import com.bleplx.adapter.utils.UUIDConverter; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; public class CharacteristicToJsObjectConverter extends JSObjectConverter { - private interface Metadata { - String ID = "id"; - String UUID = "uuid"; - String SERVICE_ID = "serviceID"; - String SERVICE_UUID = "serviceUUID"; - String DEVICE_ID = "deviceID"; - String IS_READABLE = "isReadable"; - String IS_WRITABLE_WITH_RESPONSE = "isWritableWithResponse"; - String IS_WRITABLE_WITHOUT_RESPONSE = "isWritableWithoutResponse"; - String IS_NOTIFIABLE = "isNotifiable"; - String IS_NOTIFYING = "isNotifying"; - String IS_INDICATABLE = "isIndicatable"; - String VALUE = "value"; - } + private interface Metadata { + String ID = "id"; + String UUID = "uuid"; + String SERVICE_ID = "serviceID"; + String SERVICE_UUID = "serviceUUID"; + String DEVICE_ID = "deviceID"; + String IS_READABLE = "isReadable"; + String IS_WRITABLE_WITH_RESPONSE = "isWritableWithResponse"; + String IS_WRITABLE_WITHOUT_RESPONSE = "isWritableWithoutResponse"; + String IS_NOTIFIABLE = "isNotifiable"; + String IS_NOTIFYING = "isNotifying"; + String IS_INDICATABLE = "isIndicatable"; + String VALUE = "value"; + } - @Override - public WritableMap toJSObject(Characteristic characteristic) { - WritableMap js = Arguments.createMap(); + @Override + public WritableMap toJSObject(Characteristic characteristic) { + WritableMap js = Arguments.createMap(); - js.putInt(Metadata.ID, characteristic.getId()); - js.putString(Metadata.UUID, UUIDConverter.fromUUID(characteristic.getUuid())); - js.putInt(Metadata.SERVICE_ID, characteristic.getServiceID()); - js.putString(Metadata.SERVICE_UUID, UUIDConverter.fromUUID(characteristic.getServiceUUID())); - js.putString(Metadata.DEVICE_ID, characteristic.getDeviceId()); - js.putBoolean(Metadata.IS_READABLE, characteristic.isReadable()); - js.putBoolean(Metadata.IS_WRITABLE_WITH_RESPONSE, characteristic.isWritableWithResponse()); - js.putBoolean(Metadata.IS_WRITABLE_WITHOUT_RESPONSE, characteristic.isWritableWithoutResponse()); - js.putBoolean(Metadata.IS_NOTIFIABLE, characteristic.isNotifiable()); - js.putBoolean(Metadata.IS_INDICATABLE, characteristic.isIndicatable()); - js.putBoolean(Metadata.IS_NOTIFYING, characteristic.isNotifying()); - js.putString(Metadata.VALUE, - characteristic.getValue() != null ? - Base64Converter.encode(characteristic.getValue()) : null); - return js; - } + js.putInt(Metadata.ID, characteristic.getId()); + js.putString(Metadata.UUID, UUIDConverter.fromUUID(characteristic.getUuid())); + js.putInt(Metadata.SERVICE_ID, characteristic.getServiceID()); + js.putString(Metadata.SERVICE_UUID, UUIDConverter.fromUUID(characteristic.getServiceUUID())); + js.putString(Metadata.DEVICE_ID, characteristic.getDeviceId()); + js.putBoolean(Metadata.IS_READABLE, characteristic.isReadable()); + js.putBoolean(Metadata.IS_WRITABLE_WITH_RESPONSE, characteristic.isWritableWithResponse()); + js.putBoolean(Metadata.IS_WRITABLE_WITHOUT_RESPONSE, characteristic.isWritableWithoutResponse()); + js.putBoolean(Metadata.IS_NOTIFIABLE, characteristic.isNotifiable()); + js.putBoolean(Metadata.IS_INDICATABLE, characteristic.isIndicatable()); + js.putBoolean(Metadata.IS_NOTIFYING, characteristic.isNotifying()); + js.putString(Metadata.VALUE, + characteristic.getValue() != null ? + Base64Converter.encode(characteristic.getValue()) : null); + return js; + } } diff --git a/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java index 88cb16c0..05af9acb 100644 --- a/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/DescriptorToJsObjectConverter.java @@ -1,39 +1,39 @@ package com.bleplx.converter; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; import com.bleplx.adapter.Descriptor; import com.bleplx.adapter.utils.Base64Converter; import com.bleplx.adapter.utils.UUIDConverter; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; public class DescriptorToJsObjectConverter extends JSObjectConverter { - private interface Metadata { - String ID = "id"; - String UUID = "uuid"; - String CHARACTERISTIC_UUID = "characteristicUUID"; - String CHARACTERISTIC_ID = "characteristicID"; - String SERVICE_ID = "serviceID"; - String SERVICE_UUID = "serviceUUID"; - String DEVICE_ID = "deviceID"; - String VALUE = "value"; - } + private interface Metadata { + String ID = "id"; + String UUID = "uuid"; + String CHARACTERISTIC_UUID = "characteristicUUID"; + String CHARACTERISTIC_ID = "characteristicID"; + String SERVICE_ID = "serviceID"; + String SERVICE_UUID = "serviceUUID"; + String DEVICE_ID = "deviceID"; + String VALUE = "value"; + } - @Override - public WritableMap toJSObject(Descriptor descriptor) { - WritableMap js = Arguments.createMap(); - js.putInt(Metadata.ID, descriptor.getId()); - js.putString(Metadata.UUID, UUIDConverter.fromUUID(descriptor.getUuid())); - js.putInt(Metadata.CHARACTERISTIC_ID, descriptor.getCharacteristicId()); - js.putString(Metadata.CHARACTERISTIC_UUID, UUIDConverter.fromUUID(descriptor.getCharacteristicUuid())); - js.putInt(Metadata.SERVICE_ID, descriptor.getServiceId()); - js.putString(Metadata.SERVICE_UUID, UUIDConverter.fromUUID(descriptor.getServiceUuid())); - js.putString(Metadata.DEVICE_ID, descriptor.getDeviceId()); + @Override + public WritableMap toJSObject(Descriptor descriptor) { + WritableMap js = Arguments.createMap(); + js.putInt(Metadata.ID, descriptor.getId()); + js.putString(Metadata.UUID, UUIDConverter.fromUUID(descriptor.getUuid())); + js.putInt(Metadata.CHARACTERISTIC_ID, descriptor.getCharacteristicId()); + js.putString(Metadata.CHARACTERISTIC_UUID, UUIDConverter.fromUUID(descriptor.getCharacteristicUuid())); + js.putInt(Metadata.SERVICE_ID, descriptor.getServiceId()); + js.putString(Metadata.SERVICE_UUID, UUIDConverter.fromUUID(descriptor.getServiceUuid())); + js.putString(Metadata.DEVICE_ID, descriptor.getDeviceId()); - if (descriptor.getValue() == null) { - descriptor.setValueFromCache(); - } - js.putString(Metadata.VALUE, descriptor.getValue() != null ? Base64Converter.encode(descriptor.getValue()) : null); - return js; + if (descriptor.getValue() == null) { + descriptor.setValueFromCache(); } + js.putString(Metadata.VALUE, descriptor.getValue() != null ? Base64Converter.encode(descriptor.getValue()) : null); + return js; + } } diff --git a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java index f439c761..735be0ec 100644 --- a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java @@ -1,55 +1,54 @@ package com.bleplx.converter; +import com.bleplx.adapter.Device; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.bleplx.adapter.Device; -import com.bleplx.adapter.utils.Constants; public class DeviceToJsObjectConverter extends JSObjectConverter { - private interface Metadata { - String ID = "id"; - String NAME = "name"; - String RSSI = "rssi"; - String MTU = "mtu"; - - String MANUFACTURER_DATA = "manufacturerData"; - String SERVICE_DATA = "serviceData"; - String SERVICE_UUIDS = "serviceUUIDs"; - String LOCAL_NAME = "localName"; - String TX_POWER_LEVEL = "txPowerLevel"; - String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs"; - String IS_CONNECTABLE = "isConnectable"; - String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs"; + private interface Metadata { + String ID = "id"; + String NAME = "name"; + String RSSI = "rssi"; + String MTU = "mtu"; + + String MANUFACTURER_DATA = "manufacturerData"; + String SERVICE_DATA = "serviceData"; + String SERVICE_UUIDS = "serviceUUIDs"; + String LOCAL_NAME = "localName"; + String TX_POWER_LEVEL = "txPowerLevel"; + String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs"; + String IS_CONNECTABLE = "isConnectable"; + String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs"; + } + + + @Override + public WritableMap toJSObject(Device value) { + WritableMap result = Arguments.createMap(); + result.putString(Metadata.ID, value.getId()); + result.putString(Metadata.NAME, value.getName()); + if (value.getRssi() != null) { + result.putInt(Metadata.RSSI, value.getRssi()); + } else { + result.putNull(Metadata.RSSI); } - - - @Override - public WritableMap toJSObject(Device value) { - WritableMap result = Arguments.createMap(); - result.putString(Metadata.ID, value.getId()); - result.putString(Metadata.NAME, value.getName()); - if (value.getRssi() != null) { - result.putInt(Metadata.RSSI, value.getRssi()); - } else { - result.putNull(Metadata.RSSI); - } - if (value.getMtu() != null) { - result.putInt(Metadata.MTU, value.getMtu()); - } else { - result.putNull(Metadata.MTU); - } - - // Advertisement data is not set - result.putNull(Metadata.MANUFACTURER_DATA); - result.putNull(Metadata.SERVICE_DATA); - result.putNull(Metadata.SERVICE_UUIDS); - result.putNull(Metadata.LOCAL_NAME); - result.putNull(Metadata.TX_POWER_LEVEL); - result.putNull(Metadata.SOLICITED_SERVICE_UUIDS); - result.putNull(Metadata.IS_CONNECTABLE); - result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS); - - return result; + if (value.getMtu() != null) { + result.putInt(Metadata.MTU, value.getMtu()); + } else { + result.putNull(Metadata.MTU); } + + // Advertisement data is not set + result.putNull(Metadata.MANUFACTURER_DATA); + result.putNull(Metadata.SERVICE_DATA); + result.putNull(Metadata.SERVICE_UUIDS); + result.putNull(Metadata.LOCAL_NAME); + result.putNull(Metadata.TX_POWER_LEVEL); + result.putNull(Metadata.SOLICITED_SERVICE_UUIDS); + result.putNull(Metadata.IS_CONNECTABLE); + result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS); + + return result; + } } diff --git a/android/src/main/java/com/bleplx/converter/JSObjectConverter.java b/android/src/main/java/com/bleplx/converter/JSObjectConverter.java index dcaa39e0..d72130c0 100644 --- a/android/src/main/java/com/bleplx/converter/JSObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/JSObjectConverter.java @@ -6,12 +6,12 @@ abstract class JSObjectConverter { - abstract public WritableMap toJSObject(T value); + abstract public WritableMap toJSObject(T value); - public WritableArray toJSCallback(T value) { - WritableArray array = Arguments.createArray(); - array.pushNull(); - array.pushMap(toJSObject(value)); - return array; - } -} \ No newline at end of file + public WritableArray toJSCallback(T value) { + WritableArray array = Arguments.createArray(); + array.pushNull(); + array.pushMap(toJSObject(value)); + return array; + } +} diff --git a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java index 87ed35ea..15d743be 100644 --- a/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/ScanResultToJsObjectConverter.java @@ -2,94 +2,94 @@ import androidx.annotation.NonNull; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; import com.bleplx.adapter.AdvertisementData; import com.bleplx.adapter.ScanResult; import com.bleplx.adapter.utils.Base64Converter; import com.bleplx.adapter.utils.UUIDConverter; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; import java.util.Map; import java.util.UUID; public class ScanResultToJsObjectConverter extends JSObjectConverter { - interface Metadata { - String ID = "id"; - String NAME = "name"; - String RSSI = "rssi"; - String MTU = "mtu"; + interface Metadata { + String ID = "id"; + String NAME = "name"; + String RSSI = "rssi"; + String MTU = "mtu"; - String MANUFACTURER_DATA = "manufacturerData"; - String SERVICE_DATA = "serviceData"; - String SERVICE_UUIDS = "serviceUUIDs"; - String LOCAL_NAME = "localName"; - String TX_POWER_LEVEL = "txPowerLevel"; - String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs"; - String IS_CONNECTABLE = "isConnectable"; - String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs"; - } + String MANUFACTURER_DATA = "manufacturerData"; + String SERVICE_DATA = "serviceData"; + String SERVICE_UUIDS = "serviceUUIDs"; + String LOCAL_NAME = "localName"; + String TX_POWER_LEVEL = "txPowerLevel"; + String SOLICITED_SERVICE_UUIDS = "solicitedServiceUUIDs"; + String IS_CONNECTABLE = "isConnectable"; + String OVERFLOW_SERVICE_UUIDS = "overflowServiceUUIDs"; + } - @Override - public WritableMap toJSObject(@NonNull ScanResult scanResult) { - WritableMap result = Arguments.createMap(); - result.putString(Metadata.ID, scanResult.getDeviceId()); - result.putString(Metadata.NAME, scanResult.getDeviceName()); - result.putInt(Metadata.RSSI, scanResult.getRssi()); - result.putInt(Metadata.MTU, scanResult.getMtu()); - result.putBoolean(Metadata.IS_CONNECTABLE, scanResult.isConnectable()); + @Override + public WritableMap toJSObject(@NonNull ScanResult scanResult) { + WritableMap result = Arguments.createMap(); + result.putString(Metadata.ID, scanResult.getDeviceId()); + result.putString(Metadata.NAME, scanResult.getDeviceName()); + result.putInt(Metadata.RSSI, scanResult.getRssi()); + result.putInt(Metadata.MTU, scanResult.getMtu()); + result.putBoolean(Metadata.IS_CONNECTABLE, scanResult.isConnectable()); - AdvertisementData advData = scanResult.getAdvertisementData(); - result.putString(Metadata.MANUFACTURER_DATA, - advData.getManufacturerData() != null ? - Base64Converter.encode(advData.getManufacturerData()) : null); + AdvertisementData advData = scanResult.getAdvertisementData(); + result.putString(Metadata.MANUFACTURER_DATA, + advData.getManufacturerData() != null ? + Base64Converter.encode(advData.getManufacturerData()) : null); - if (advData.getServiceData() != null) { - WritableMap serviceData = Arguments.createMap(); - for (Map.Entry entry : advData.getServiceData().entrySet()) { - serviceData.putString(UUIDConverter.fromUUID(entry.getKey()), - Base64Converter.encode(entry.getValue())); - } - result.putMap(Metadata.SERVICE_DATA, serviceData); - } else { - result.putNull(Metadata.SERVICE_DATA); - } - - if (advData.getServiceUUIDs() != null) { - WritableArray serviceUUIDs = Arguments.createArray(); - for (UUID serviceUUID : advData.getServiceUUIDs()) { - serviceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID)); - } - result.putArray(Metadata.SERVICE_UUIDS, serviceUUIDs); - } else { - result.putNull(Metadata.SERVICE_UUIDS); - } + if (advData.getServiceData() != null) { + WritableMap serviceData = Arguments.createMap(); + for (Map.Entry entry : advData.getServiceData().entrySet()) { + serviceData.putString(UUIDConverter.fromUUID(entry.getKey()), + Base64Converter.encode(entry.getValue())); + } + result.putMap(Metadata.SERVICE_DATA, serviceData); + } else { + result.putNull(Metadata.SERVICE_DATA); + } - if (advData.getLocalName() != null) { - result.putString(Metadata.LOCAL_NAME, advData.getLocalName()); - } else { - result.putNull(Metadata.LOCAL_NAME); - } + if (advData.getServiceUUIDs() != null) { + WritableArray serviceUUIDs = Arguments.createArray(); + for (UUID serviceUUID : advData.getServiceUUIDs()) { + serviceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID)); + } + result.putArray(Metadata.SERVICE_UUIDS, serviceUUIDs); + } else { + result.putNull(Metadata.SERVICE_UUIDS); + } - if (advData.getTxPowerLevel() != null) { - result.putInt(Metadata.TX_POWER_LEVEL, advData.getTxPowerLevel()); - } else { - result.putNull(Metadata.TX_POWER_LEVEL); - } + if (advData.getLocalName() != null) { + result.putString(Metadata.LOCAL_NAME, advData.getLocalName()); + } else { + result.putNull(Metadata.LOCAL_NAME); + } - if (advData.getSolicitedServiceUUIDs() != null) { - WritableArray solicitedServiceUUIDs = Arguments.createArray(); - for (UUID serviceUUID : advData.getSolicitedServiceUUIDs()) { - solicitedServiceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID)); - } - result.putArray(Metadata.SOLICITED_SERVICE_UUIDS, solicitedServiceUUIDs); - } else { - result.putNull(Metadata.SOLICITED_SERVICE_UUIDS); - } + if (advData.getTxPowerLevel() != null) { + result.putInt(Metadata.TX_POWER_LEVEL, advData.getTxPowerLevel()); + } else { + result.putNull(Metadata.TX_POWER_LEVEL); + } - // Attributes which are not accessible on Android - result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS); - return result; + if (advData.getSolicitedServiceUUIDs() != null) { + WritableArray solicitedServiceUUIDs = Arguments.createArray(); + for (UUID serviceUUID : advData.getSolicitedServiceUUIDs()) { + solicitedServiceUUIDs.pushString(UUIDConverter.fromUUID(serviceUUID)); + } + result.putArray(Metadata.SOLICITED_SERVICE_UUIDS, solicitedServiceUUIDs); + } else { + result.putNull(Metadata.SOLICITED_SERVICE_UUIDS); } + + // Attributes which are not accessible on Android + result.putNull(Metadata.OVERFLOW_SERVICE_UUIDS); + return result; + } } diff --git a/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java index f1af91b3..68853233 100644 --- a/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/ServiceToJsObjectConverter.java @@ -1,26 +1,26 @@ package com.bleplx.converter; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; import com.bleplx.adapter.Service; import com.bleplx.adapter.utils.UUIDConverter; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; public class ServiceToJsObjectConverter extends JSObjectConverter { - private interface Metadata { - String ID = "id"; - String UUID = "uuid"; - String DEVICE_ID = "deviceID"; - String IS_PRIMARY = "isPrimary"; - } + private interface Metadata { + String ID = "id"; + String UUID = "uuid"; + String DEVICE_ID = "deviceID"; + String IS_PRIMARY = "isPrimary"; + } - @Override - public WritableMap toJSObject(Service service) { - WritableMap result = Arguments.createMap(); - result.putInt(Metadata.ID, service.getId()); - result.putString(Metadata.UUID, UUIDConverter.fromUUID(service.getUuid())); - result.putString(Metadata.DEVICE_ID, service.getDeviceID()); - result.putBoolean(Metadata.IS_PRIMARY, service.isPrimary()); - return result; - } + @Override + public WritableMap toJSObject(Service service) { + WritableMap result = Arguments.createMap(); + result.putInt(Metadata.ID, service.getId()); + result.putString(Metadata.UUID, UUIDConverter.fromUUID(service.getUuid())); + result.putString(Metadata.DEVICE_ID, service.getDeviceID()); + result.putBoolean(Metadata.IS_PRIMARY, service.isPrimary()); + return result; + } } diff --git a/android/src/main/java/com/bleplx/utils/Base64Converter.java b/android/src/main/java/com/bleplx/utils/Base64Converter.java index 62542d81..2f5b0d37 100644 --- a/android/src/main/java/com/bleplx/utils/Base64Converter.java +++ b/android/src/main/java/com/bleplx/utils/Base64Converter.java @@ -3,10 +3,11 @@ import android.util.Base64; public class Base64Converter { - public static String encode(byte[] bytes) { - return Base64.encodeToString(bytes, Base64.NO_WRAP); - } - public static byte[] decode(String base64) { - return Base64.decode(base64, Base64.NO_WRAP); - } + public static String encode(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.NO_WRAP); + } + + public static byte[] decode(String base64) { + return Base64.decode(base64, Base64.NO_WRAP); + } } diff --git a/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java b/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java index 334fa0eb..b6772414 100644 --- a/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java +++ b/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java @@ -3,11 +3,11 @@ import com.facebook.react.bridge.ReadableArray; public class ReadableArrayConverter { - public static String[] toStringArray(ReadableArray readableArray) { - String[] stringArray = new String[readableArray.size()]; - for (int i = 0; i < readableArray.size(); ++i) { - stringArray[i] = readableArray.getString(i); - } - return stringArray; + public static String[] toStringArray(ReadableArray readableArray) { + String[] stringArray = new String[readableArray.size()]; + for (int i = 0; i < readableArray.size(); ++i) { + stringArray[i] = readableArray.getString(i); } + return stringArray; + } } diff --git a/android/src/main/java/com/bleplx/utils/SafePromise.java b/android/src/main/java/com/bleplx/utils/SafePromise.java index 98a22b79..0203473c 100644 --- a/android/src/main/java/com/bleplx/utils/SafePromise.java +++ b/android/src/main/java/com/bleplx/utils/SafePromise.java @@ -7,47 +7,47 @@ import javax.annotation.Nullable; public class SafePromise { - private Promise promise; - private AtomicBoolean isFinished = new AtomicBoolean(); + private Promise promise; + private AtomicBoolean isFinished = new AtomicBoolean(); - public SafePromise(Promise promise) { - this.promise = promise; - } + public SafePromise(Promise promise) { + this.promise = promise; + } - public void resolve(@Nullable Object value) { - if (isFinished.compareAndSet(false, true)) { - promise.resolve(value); - } + public void resolve(@Nullable Object value) { + if (isFinished.compareAndSet(false, true)) { + promise.resolve(value); } + } - public void reject(String code, String message) { - if (isFinished.compareAndSet(false, true)) { - promise.reject(code, message); - } + public void reject(String code, String message) { + if (isFinished.compareAndSet(false, true)) { + promise.reject(code, message); } + } - public void reject(String code, Throwable e) { - if (isFinished.compareAndSet(false, true)) { - promise.reject(code, e); - } + public void reject(String code, Throwable e) { + if (isFinished.compareAndSet(false, true)) { + promise.reject(code, e); } + } - public void reject(String code, String message, Throwable e) { - if (isFinished.compareAndSet(false, true)) { - promise.reject(code, message, e); - } + public void reject(String code, String message, Throwable e) { + if (isFinished.compareAndSet(false, true)) { + promise.reject(code, message, e); } + } - @Deprecated - public void reject(String message) { - if (isFinished.compareAndSet(false, true)) { - promise.reject(message); - } + @Deprecated + public void reject(String message) { + if (isFinished.compareAndSet(false, true)) { + promise.reject(message); } + } - public void reject(Throwable reason) { - if (isFinished.compareAndSet(false, true)) { - promise.reject(reason); - } + public void reject(Throwable reason) { + if (isFinished.compareAndSet(false, true)) { + promise.reject(reason); } + } } diff --git a/android/src/main/java/com/bleplx/utils/UUIDConverter.java b/android/src/main/java/com/bleplx/utils/UUIDConverter.java index 3514c0da..d36ad63e 100644 --- a/android/src/main/java/com/bleplx/utils/UUIDConverter.java +++ b/android/src/main/java/com/bleplx/utils/UUIDConverter.java @@ -6,67 +6,65 @@ public class UUIDConverter { - private static String baseUUIDPrefix = "0000"; - private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB"; + private static String baseUUIDPrefix = "0000"; + private static String baseUUIDSuffix = "-0000-1000-8000-00805F9B34FB"; - public static UUID convert(String sUUID) { - if (sUUID == null) return null; + public static UUID convert(String sUUID) { + if (sUUID == null) return null; - if (sUUID.length() == 4) { - sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; - } - else if (sUUID.length() == 8) { - sUUID = sUUID + baseUUIDSuffix; - } + if (sUUID.length() == 4) { + sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; + } else if (sUUID.length() == 8) { + sUUID = sUUID + baseUUIDSuffix; + } - try { - return UUID.fromString(sUUID); - } catch (Throwable e) { - return null; - } + try { + return UUID.fromString(sUUID); + } catch (Throwable e) { + return null; } + } - public static UUID[] convert(String... sUUIDs) { - UUID[] UUIDs = new UUID[sUUIDs.length]; - for (int i = 0; i < sUUIDs.length; i++) { + public static UUID[] convert(String... sUUIDs) { + UUID[] UUIDs = new UUID[sUUIDs.length]; + for (int i = 0; i < sUUIDs.length; i++) { - if (sUUIDs[i] == null) return null; + if (sUUIDs[i] == null) return null; - if (sUUIDs[i].length() == 4) { - sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix; - } - else if (sUUIDs[i].length() == 8) { - sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix; - } + if (sUUIDs[i].length() == 4) { + sUUIDs[i] = baseUUIDPrefix + sUUIDs[i] + baseUUIDSuffix; + } else if (sUUIDs[i].length() == 8) { + sUUIDs[i] = sUUIDs[i] + baseUUIDSuffix; + } - try { - UUIDs[i] = UUID.fromString(sUUIDs[i]); - } catch (Throwable e) { - return null; - } - } - return UUIDs; + try { + UUIDs[i] = UUID.fromString(sUUIDs[i]); + } catch (Throwable e) { + return null; + } } + return UUIDs; + } - public static UUID[] convert(ReadableArray aUUIDs) { - UUID[] UUIDs = new UUID[aUUIDs.size()]; - for (int i = 0; i < aUUIDs.size(); i++) { - try { - String sUUID = aUUIDs.getString(i); - if (sUUID.length() == 4) { - sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; - } else if (sUUID.length() == 8) { - sUUID = sUUID + baseUUIDSuffix; - } - UUIDs[i] = UUID.fromString(sUUID); - } catch (Throwable e) { - return null; - } + public static UUID[] convert(ReadableArray aUUIDs) { + UUID[] UUIDs = new UUID[aUUIDs.size()]; + for (int i = 0; i < aUUIDs.size(); i++) { + try { + String sUUID = aUUIDs.getString(i); + if (sUUID.length() == 4) { + sUUID = baseUUIDPrefix + sUUID + baseUUIDSuffix; + } else if (sUUID.length() == 8) { + sUUID = sUUID + baseUUIDSuffix; } - return UUIDs; + UUIDs[i] = UUID.fromString(sUUID); + } catch (Throwable e) { + return null; + } } + return UUIDs; + } - public static String fromUUID(UUID uuid) { - return uuid.toString().toLowerCase(); - } + public static String fromUUID(UUID uuid) { + return uuid.toString().toLowerCase(); + } }