diff --git a/.eslintignore b/.eslintignore
index 094ca02f8..68b1c204c 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,5 @@
/**/node_modules/*
node_modules/
docs/**
-plugin/build
\ No newline at end of file
+plugin/build
+lib/**
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index c68541a2e..000000000
--- 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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5792d500d..c8cd8c7b5 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 5b1c71cc4..1bf80a1c5 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 28c103c55..0181b51ad 100644
--- a/README.md
+++ b/README.md
@@ -165,7 +165,7 @@ The plugin provides props for extra customization. Every time you change the pro
}
```
-1. (Optional) In `AndroidManifest.xml`, add Bluetooth permissions and update ``:
+1. In `AndroidManifest.xml`, add Bluetooth permissions and update ``:
```xml
@@ -189,6 +189,23 @@ The plugin provides props for extra customization. Every time you change the pro
...
```
+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
## Contributions
diff --git a/android/build.gradle b/android/build.gradle
index b17854f2e..54b42751e 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:0.2.0'
+ 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 ab9627172..e418b9164 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/AndroidManifestNew.xml b/android/src/main/AndroidManifestNew.xml
index a2f47b605..0bfa73ba6 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 229a7023c..ca1d171ca 100644
--- a/android/src/main/java/com/bleplx/BlePlxModule.java
+++ b/android/src/main/java/com/bleplx/BlePlxModule.java
@@ -1,7 +1,32 @@
package com.bleplx;
+import android.app.Activity;
+
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+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;
+import com.bleplx.converter.DeviceToJsObjectConverter;
+import com.bleplx.converter.ScanResultToJsObjectConverter;
+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;
@@ -14,939 +39,931 @@
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.converter.BleErrorToJsObjectConverter;
-import com.bleplx.converter.CharacteristicToJsObjectConverter;
-import com.bleplx.converter.DescriptorToJsObjectConverter;
-import com.bleplx.converter.DeviceToJsObjectConverter;
-import com.bleplx.converter.ScanResultToJsObjectConverter;
-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);
- }
-
- @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);
+ 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);
}
- 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;
-
- 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");
- }
- }
-
- bleAdapter.startDeviceScan(
- filteredUUIDs != null ? ReadableArrayConverter.toStringArray(filteredUUIDs) : null,
- scanMode, callbackType,
- 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));
+ }
+ });
+ }
+
+ @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);
}
-
- }
-
- @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));
+ }, new OnEventCallback() {
+ @Override
+ public void onEvent(Integer data) {
+ sendEvent(Event.RestoreStateEvent, null);
}
- }
-
- @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 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));
}
- }
-
- @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));
+ }, new OnErrorCallback() {
+ @Override
+ public void onError(BleError error) {
+ sendEvent(Event.ScanEvent, errorConverter.toJSCallback(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 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));
+ }
+
+ }
+
+ @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 readDescriptorForDevice(final String deviceId,
- final String serviceUUID,
+ @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.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 215fbabe4..cadcde882 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
new file mode 100644
index 000000000..21a6d51f2
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/AdvertisementData.java
@@ -0,0 +1,251 @@
+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 000000000..6c26d45cb
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/BleAdapter.java
@@ -0,0 +1,251 @@
+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,
+ 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
new file mode 100644
index 000000000..93223f140
--- /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 000000000..57fc3a633
--- /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/BleModule.java b/android/src/main/java/com/bleplx/adapter/BleModule.java
new file mode 100755
index 000000000..67c2bd87a
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/BleModule.java
@@ -0,0 +1,1569 @@
+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,
+ 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;
+ }
+
+ 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) {
+ RxBleLog.e(e, "Error while checking if device is connected");
+ 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,
+ 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);
+ });
+
+ 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 000000000..7208813a2
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/Characteristic.java
@@ -0,0 +1,153 @@
+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 000000000..51b0044c0
--- /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 000000000..56e0ca93d
--- /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 000000000..5057497d4
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/Descriptor.java
@@ -0,0 +1,117 @@
+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 000000000..178709e03
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/Device.java
@@ -0,0 +1,80 @@
+package com.bleplx.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+import java.util.UUID;
+
+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 000000000..a073aff4d
--- /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 000000000..bc6330c8e
--- /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 000000000..27a396917
--- /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 000000000..eafefc071
--- /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 000000000..53eac5a86
--- /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 000000000..b23eaa2bd
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/Service.java
@@ -0,0 +1,58 @@
+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 000000000..802528c86
--- /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 000000000..fe8a511d2
--- /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 000000000..998647c9d
--- /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 000000000..96bb3c66d
--- /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 000000000..bc94b7f81
--- /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 000000000..814fe773d
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/utils/Base64Converter.java
@@ -0,0 +1,13 @@
+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 000000000..111ec29a4
--- /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 000000000..048cefe92
--- /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 000000000..83e947296
--- /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 000000000..116124e10
--- /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 000000000..721b9f6f1
--- /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 000000000..eb249fd1b
--- /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 000000000..68f7fff07
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/utils/RefreshGattCustomOperation.java
@@ -0,0 +1,50 @@
+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 000000000..fa19f4e37
--- /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 000000000..b4b2f9f0c
--- /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 000000000..96247f8ce
--- /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 000000000..8e943e195
--- /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 000000000..2cf44175a
--- /dev/null
+++ b/android/src/main/java/com/bleplx/adapter/utils/mapper/RxScanResultToScanResultMapper.java
@@ -0,0 +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,
+ 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/BleErrorToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/BleErrorToJsObjectConverter.java
index ac15a1b76..370f13524 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.polidea.multiplatformbleadapter.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("\"");
}
-}
\ 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 1be25281d..270b4f62e 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.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;
-import com.polidea.multiplatformbleadapter.Characteristic;
-import com.polidea.multiplatformbleadapter.utils.Base64Converter;
-import com.polidea.multiplatformbleadapter.utils.UUIDConverter;
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 68e9f52f8..05af9acb0 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.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;
-import com.polidea.multiplatformbleadapter.Descriptor;
-import com.polidea.multiplatformbleadapter.utils.Base64Converter;
-import com.polidea.multiplatformbleadapter.utils.UUIDConverter;
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();
}
-}
\ No newline at end of file
+ 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 e4e7166cd..735be0ec0 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.polidea.multiplatformbleadapter.Device;
-import com.polidea.multiplatformbleadapter.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 dcaa39e04..d72130c08 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 eabe3dbdb..15d743be9 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.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 com.polidea.multiplatformbleadapter.AdvertisementData;
-import com.polidea.multiplatformbleadapter.ScanResult;
-import com.polidea.multiplatformbleadapter.utils.Base64Converter;
-import com.polidea.multiplatformbleadapter.utils.UUIDConverter;
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";
- }
-
- @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());
+ 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";
+ }
- AdvertisementData advData = scanResult.getAdvertisementData();
- result.putString(Metadata.MANUFACTURER_DATA,
- advData.getManufacturerData() != null ?
- Base64Converter.encode(advData.getManufacturerData()) : null);
+ @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());
- 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);
- }
+ AdvertisementData advData = scanResult.getAdvertisementData();
+ result.putString(Metadata.MANUFACTURER_DATA,
+ advData.getManufacturerData() != null ?
+ Base64Converter.encode(advData.getManufacturerData()) : null);
- 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.IS_CONNECTABLE);
- 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 f713ac7ab..68853233e 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.bleplx.adapter.Service;
+import com.bleplx.adapter.utils.UUIDConverter;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
-import com.polidea.multiplatformbleadapter.Service;
-import com.polidea.multiplatformbleadapter.utils.UUIDConverter;
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 62542d811..2f5b0d37d 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 334fa0ebb..b6772414b 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 98a22b795..0203473c8 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 3514c0dad..d36ad63e4 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();
+ }
}
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index b029d1b0d..0e1447869 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/components/atoms/Button/Button.styled.tsx b/example/src/components/atoms/Button/Button.styled.tsx
index c8cc1f6b9..73a322dc1 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/components/atoms/TestStateDisplay/TestStateDisplay.tsx b/example/src/components/atoms/TestStateDisplay/TestStateDisplay.tsx
index 44f88be0b..293665143 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 000000000..66395ca69
--- /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 adbabf631..8efa01a24 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 d2c5e85bc..f59c86c78 100644
--- a/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx
+++ b/example/src/screens/MainStack/DashboardScreen/DashboardScreen.tsx
@@ -75,10 +75,25 @@ 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')} />
+ 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 1784f1a93..1b3715b33 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 afb59930b..20784153f 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 2a22102bc..35fdd94a8 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,12 +78,22 @@ 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')
}
- 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)
@@ -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 => {
@@ -393,14 +410,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
)
}
}
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 cc4015559..9f16a5b1f 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)
}
diff --git a/src/index.d.ts b/src/index.d.ts
index f6e460d3a..c313d5dc0 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
}
/**