From 5fe5079b357d80d448c5694b22943f693e4f73ad Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski Date: Wed, 31 Jul 2024 22:16:30 +0200 Subject: [PATCH 1/9] Squashed merge: make BleManager a singleton --- .../MainStack/InstanceDestroyScreen/utils.ts | 4 ++-- src/BleManager.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/example/src/screens/MainStack/InstanceDestroyScreen/utils.ts b/example/src/screens/MainStack/InstanceDestroyScreen/utils.ts index 3b0de206..8f73e0f7 100644 --- a/example/src/screens/MainStack/InstanceDestroyScreen/utils.ts +++ b/example/src/screens/MainStack/InstanceDestroyScreen/utils.ts @@ -15,7 +15,7 @@ export const functionsToTest: { name: string; functionToCall: () => Promise }, { name: 'state', - functionToCall: BLEService.manager.state + functionToCall: () => BLEService.manager.state() }, { name: 'startDeviceScan', @@ -23,7 +23,7 @@ export const functionsToTest: { name: string; functionToCall: () => Promise }, { name: 'stopDeviceScan', - functionToCall: BLEService.manager.stopDeviceScan + functionToCall: () => BLEService.manager.stopDeviceScan() }, { name: 'requestConnectionPriorityForDevice', diff --git a/src/BleManager.js b/src/BleManager.js index 0eab2695..f0b1345b 100644 --- a/src/BleManager.js +++ b/src/BleManager.js @@ -65,10 +65,18 @@ export class BleManager { // Map of error codes to error messages _errorCodesToMessagesMapping: BleErrorCodeMessageMapping + static sharedInstance: BleManager | null = null + /** * Creates an instance of {@link BleManager}. + * It will return already created instance if it was created before. + * If you want to create a new instance to for example use different options, you have to call {@link #blemanagerdestroy|destroy()} on the previous one. */ constructor(options: BleManagerOptions = {}) { + if (BleManager.sharedInstance !== null) { + return BleManager.sharedInstance + } + this._eventEmitter = new EventEmitter(BleModule) this._uniqueId = 0 this._activePromises = {} @@ -98,6 +106,7 @@ export class BleManager { : BleErrorCodeMessage BleModule.createClient(options.restoreStateIdentifier || null) + BleManager.sharedInstance = this } /** @@ -146,6 +155,10 @@ export class BleManager { } this._destroySubscriptions() + if (BleManager.sharedInstance) { + BleManager.sharedInstance = null + } + // Destroy all promises this._destroyPromises() From 14733246ebb286d7e701154a5bfcabc8375f9d8c Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski Date: Wed, 31 Jul 2024 22:17:34 +0200 Subject: [PATCH 2/9] Squashed merge: hide _manager field --- src/Characteristic.js | 3 ++- src/Descriptor.js | 3 ++- src/Device.js | 3 ++- src/Service.js | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Characteristic.js b/src/Characteristic.js index 536f85fb..7af37b0c 100644 --- a/src/Characteristic.js +++ b/src/Characteristic.js @@ -72,7 +72,8 @@ export class Characteristic implements NativeCharacteristic { * @private */ constructor(nativeCharacteristic: NativeCharacteristic, manager: BleManager) { - Object.assign(this, nativeCharacteristic, { _manager: manager }) + Object.assign(this, nativeCharacteristic) + Object.defineProperty(this, '_manager', { value: manager, enumerable: false }) } /** diff --git a/src/Descriptor.js b/src/Descriptor.js index 536ca67c..d8d47622 100644 --- a/src/Descriptor.js +++ b/src/Descriptor.js @@ -54,7 +54,8 @@ export class Descriptor implements NativeDescriptor { * @private */ constructor(nativeDescriptor: NativeDescriptor, manager: BleManager) { - Object.assign(this, nativeDescriptor, { _manager: manager }) + Object.assign(this, nativeDescriptor) + Object.defineProperty(this, '_manager', { value: manager, enumerable: false }) } /** diff --git a/src/Device.js b/src/Device.js index 16159a7a..3340a89c 100644 --- a/src/Device.js +++ b/src/Device.js @@ -98,7 +98,8 @@ export class Device implements NativeDevice { * @private */ constructor(nativeDevice: NativeDevice, manager: BleManager) { - Object.assign(this, nativeDevice, { _manager: manager }) + Object.assign(this, nativeDevice) + Object.defineProperty(this, '_manager', { value: manager, enumerable: false }) } /** diff --git a/src/Service.js b/src/Service.js index 8d4305ff..172e95be 100644 --- a/src/Service.js +++ b/src/Service.js @@ -43,7 +43,8 @@ export class Service implements NativeService { * @ignore */ constructor(nativeService: NativeService, manager: BleManager) { - Object.assign(this, nativeService, { _manager: manager }) + Object.assign(this, nativeService) + Object.defineProperty(this, '_manager', { value: manager, enumerable: false }) } /** From 36f0b56d56c06137ee735b94689966afdc19e774 Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski Date: Wed, 31 Jul 2024 22:23:03 +0200 Subject: [PATCH 3/9] Squashed merge: add connection timeout test --- example/ios/Podfile.lock | 33 +++++++++++ example/package.json | 9 +-- example/src/App.tsx | 1 + .../DeviceConnectDisconnectTestScreen.tsx | 59 +++++++++++++++++-- .../DeviceOnDisconnectTestScreen.tsx | 6 +- .../DevicenRFTestScreen.tsx | 8 +-- example/src/services/BLEService/BLEService.ts | 8 ++- example/src/services/index.ts | 2 + .../services/storage/persistentDeviceName.ts | 5 ++ example/src/services/storage/storage.ts | 3 + example/yarn.lock | 5 ++ 11 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 example/src/services/storage/persistentDeviceName.ts create mode 100644 example/src/services/storage/storage.ts diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b08ea407..10095e6c 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -7,6 +7,9 @@ PODS: - hermes-engine (0.74.1): - hermes-engine/Pre-built (= 0.74.1) - hermes-engine/Pre-built (0.74.1) + - MMKV (1.3.7): + - MMKVCore (~> 1.3.7) + - MMKVCore (1.3.7) - MultiplatformBleAdapter (0.2.0) - RCT-Folly (2024.01.01.00): - boost @@ -958,6 +961,28 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-mmkv (2.12.2): + - DoubleConversion + - glog + - hermes-engine + - MMKV (>= 1.3.3) + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-safe-area-context (4.10.1): - React-Core - React-nativeconfig (0.74.1) @@ -1248,6 +1273,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - react-native-ble-plx (from `../..`) + - react-native-mmkv (from `../node_modules/react-native-mmkv`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) @@ -1277,6 +1303,8 @@ DEPENDENCIES: SPEC REPOS: trunk: + - MMKV + - MMKVCore - MultiplatformBleAdapter - SocketRocket @@ -1344,6 +1372,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" react-native-ble-plx: :path: "../.." + react-native-mmkv: + :path: "../node_modules/react-native-mmkv" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" React-nativeconfig: @@ -1404,6 +1434,8 @@ SPEC CHECKSUMS: fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 hermes-engine: 16b8530de1b383cdada1476cf52d1b52f0692cbc + MMKV: 36a22a9ec84c9bb960613a089ddf6f48be9312b0 + MMKVCore: 158e61c8516401a9fac730288acb29e6fc19bbf9 MultiplatformBleAdapter: ea8bac405ec200d0ca9de0f89afef6f06fb2abbc RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: efb313d8126259e9294dc4ee0002f44a6f676aba @@ -1430,6 +1462,7 @@ SPEC CHECKSUMS: React-logger: 7e7403a2b14c97f847d90763af76b84b152b6fce React-Mapbuffer: 11029dcd47c5c9e057a4092ab9c2a8d10a496a33 react-native-ble-plx: 08539040709361221aa9f8cada60dc730b9168c5 + react-native-mmkv: 8c9a677e64a1ac89b0c6cf240feea528318b3074 react-native-safe-area-context: dcab599c527c2d7de2d76507a523d20a0b83823d React-nativeconfig: b0073a590774e8b35192fead188a36d1dca23dec React-NativeModulesApple: df46ff3e3de5b842b30b4ca8a6caae6d7c8ab09f diff --git a/example/package.json b/example/package.json index ab4192dc..fe4a001d 100644 --- a/example/package.json +++ b/example/package.json @@ -14,6 +14,7 @@ "react": "18.2.0", "react-native": "0.74.1", "react-native-base64": "^0.2.1", + "react-native-mmkv": "^2.12.2", "react-native-safe-area-context": "^4.10.1", "react-native-screens": "^3.31.1", "react-native-toast-message": "^2.1.6", @@ -23,13 +24,13 @@ "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", + "@react-native/babel-preset": "0.74.83", "@react-native/eslint-config": "^0.72.2", - "@types/react-native-base64": "^0.2.0", "@react-native/metro-config": "0.74.83", + "@react-native/typescript-config": "0.74.83", + "@types/react-native-base64": "^0.2.0", "babel-plugin-module-resolver": "^5.0.0", - "metro-react-native-babel-preset": "0.76.8", - "@react-native/babel-preset": "0.74.83", - "@react-native/typescript-config": "0.74.83" + "metro-react-native-babel-preset": "0.76.8" }, "engines": { "node": ">=18" diff --git a/example/src/App.tsx b/example/src/App.tsx index ea87f883..346dc3bb 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -4,6 +4,7 @@ import { ThemeProvider } from 'styled-components' import Toast from 'react-native-toast-message' import { commonTheme } from './theme/theme' import { Navigation } from './navigation' +import './services/storage/storage' export function App() { return ( diff --git a/example/src/screens/MainStack/DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen.tsx b/example/src/screens/MainStack/DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen.tsx index d7af5e41..2224e261 100644 --- a/example/src/screens/MainStack/DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen.tsx +++ b/example/src/screens/MainStack/DeviceConnectDisconnectTestScreen/DeviceConnectDisconnectTestScreen.tsx @@ -1,11 +1,11 @@ import React, { useRef, useState } from 'react' import type { NativeStackScreenProps } from '@react-navigation/native-stack' import { BleError, Characteristic, Device, type Subscription, type DeviceId, BleErrorCode } from 'react-native-ble-plx' -import { ScrollView } from 'react-native' +import { Alert, ScrollView } from 'react-native' import base64 from 'react-native-base64' import Toast from 'react-native-toast-message' import type { TestStateType } from '../../../types' -import { BLEService } from '../../../services' +import { BLEService, usePersistentDeviceName } from '../../../services' import type { MainStackParamList } from '../../../navigation/navigators' import { AppButton, AppTextInput, ScreenDefaultContainer, TestStateDisplay } from '../../../components/atoms' import { currentTimeCharacteristic, deviceTimeService } from '../../../consts/nRFDeviceConsts' @@ -16,9 +16,10 @@ type DeviceConnectDisconnectTestScreenProps = NativeStackScreenProps< 'DEVICE_CONNECT_DISCONNECT_TEST_SCREEN' > const NUMBER_OF_CALLS_IN_THE_TEST_SCENARIO = 10 +const CONNECTION_TIMEOUT = 5000 export function DeviceConnectDisconnectTestScreen(_props: DeviceConnectDisconnectTestScreenProps) { - const [expectedDeviceName, setExpectedDeviceName] = useState('') + const [expectedDeviceName, setExpectedDeviceName] = usePersistentDeviceName() const [testScanDevicesState, setTestScanDevicesState] = useState('WAITING') const [deviceId, setDeviceId] = useState('') const [connectCounter, setConnectCounter] = useState(0) @@ -27,11 +28,59 @@ export function DeviceConnectDisconnectTestScreen(_props: DeviceConnectDisconnec const [disconnectCounter, setDisconnectCounter] = useState(0) const [monitorMessages, setMonitorMessages] = useState([]) const monitorSubscriptionRef = useRef(null) + const [timeoutTestState, setTimeoutTestState] = useState('WAITING') + const [timeoutTestLabel, setTimeoutTestLabel] = useState('Timeout Test') + + const startTimeoutTest = async () => { + await BLEService.initializeBLE() + setTimeoutTestLabel('Running') + await BLEService.scanDevices( + async (device: Device) => { + if (checkDeviceName(device)) { + BLEService.stopDeviceScan() + Alert.alert( + 'Prepare for Timeout Test', + 'Please turn off Bluetooth on the device you want to connect to. Press OK when ready.', + [ + { + text: 'OK', + onPress: () => runTimeoutTest(device) + } + ] + ) + } + }, + [deviceTimeService] + ) + } + + const runTimeoutTest = async (device: Device) => { + setTimeoutTestState('IN_PROGRESS') + + try { + await BLEService.connectToDevice(device.id, CONNECTION_TIMEOUT) + setTimeoutTestState('ERROR') + setTimeoutTestLabel('Device was able to connect') + } catch (error) { + if ( + error instanceof Error && + (error.message === 'Operation was cancelled' || error.message === 'Operation timed out') + ) { + console.info('Timeout test successful: Connection timed out as expected') + setTimeoutTestState('DONE') + setTimeoutTestLabel('Success') + } else { + console.error('Unexpected error during timeout test:', error) + setTimeoutTestState('ERROR') + setTimeoutTestLabel('Error occurred') + } + } + } const addMonitorMessage = (message: string) => setMonitorMessages(prevMessages => [...prevMessages, message]) const checkDeviceName = (device: Device) => - device.name?.toLocaleLowerCase() === expectedDeviceName.toLocaleLowerCase() + device.name?.toLocaleLowerCase() === expectedDeviceName?.toLocaleLowerCase() const startConnectAndDiscover = async () => { setTestScanDevicesState('IN_PROGRESS') @@ -224,6 +273,8 @@ export function DeviceConnectDisconnectTestScreen(_props: DeviceConnectDisconnec value={connectInDisconnectTestCounter.toString()} /> + + startCharacteristicMonitor()} /> diff --git a/example/src/screens/MainStack/DeviceOnDisconnectTestScreen/DeviceOnDisconnectTestScreen.tsx b/example/src/screens/MainStack/DeviceOnDisconnectTestScreen/DeviceOnDisconnectTestScreen.tsx index 01847a81..42440b4a 100644 --- a/example/src/screens/MainStack/DeviceOnDisconnectTestScreen/DeviceOnDisconnectTestScreen.tsx +++ b/example/src/screens/MainStack/DeviceOnDisconnectTestScreen/DeviceOnDisconnectTestScreen.tsx @@ -5,7 +5,7 @@ import { ScrollView } from 'react-native' import Toast from 'react-native-toast-message' import { wait } from '../../../utils/wait' import type { TestStateType } from '../../../types' -import { BLEService } from '../../../services' +import { BLEService, usePersistentDeviceName } from '../../../services' import type { MainStackParamList } from '../../../navigation/navigators' import { AppButton, AppTextInput, ScreenDefaultContainer, TestStateDisplay } from '../../../components/atoms' import { deviceTimeService } from '../../../consts/nRFDeviceConsts' @@ -13,14 +13,14 @@ import { deviceTimeService } from '../../../consts/nRFDeviceConsts' type DeviceOnDisconnectTestScreenProps = NativeStackScreenProps export function DeviceOnDisconnectTestScreen(_props: DeviceOnDisconnectTestScreenProps) { - const [expectedDeviceName, setExpectedDeviceName] = useState('') + const [expectedDeviceName, setExpectedDeviceName] = usePersistentDeviceName() const [testScanDevicesState, setTestScanDevicesState] = useState('WAITING') const [deviceId, setDeviceId] = useState('') const [currentTest, setCurrentTest] = useState(null) const onDisconnectRef = useRef(null) const checkDeviceName = (device: Device) => - device.name?.toLocaleLowerCase() === expectedDeviceName.toLocaleLowerCase() + device.name?.toLocaleLowerCase() === expectedDeviceName?.toLocaleLowerCase() const connectAndDiscover = async () => { setTestScanDevicesState('IN_PROGRESS') diff --git a/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx b/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx index 1b3715b3..57938256 100644 --- a/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx +++ b/example/src/screens/MainStack/DevicenRFTestScreen/DevicenRFTestScreen.tsx @@ -4,7 +4,7 @@ import { Device, type Base64 } from 'react-native-ble-plx' import { Platform, ScrollView } from 'react-native' import base64 from 'react-native-base64' import type { TestStateType } from '../../../types' -import { BLEService } from '../../../services' +import { BLEService, usePersistentDeviceName } from '../../../services' import type { MainStackParamList } from '../../../navigation/navigators' import { AppButton, AppTextInput, ScreenDefaultContainer, TestStateDisplay } from '../../../components/atoms' import { wait } from '../../../utils/wait' @@ -22,7 +22,7 @@ import { type DevicenRFTestScreenProps = NativeStackScreenProps export function DevicenRFTestScreen(_props: DevicenRFTestScreenProps) { - const [expectedDeviceName, setExpectedDeviceName] = useState('') + const [expectedDeviceName, setExpectedDeviceName] = usePersistentDeviceName() const [testScanDevicesState, setTestScanDevicesState] = useState('WAITING') const [testDeviceConnectedState, setTestDeviceConnectedState] = useState('WAITING') const [testDiscoverServicesAndCharacteristicsFoundState, setTestDiscoverServicesAndCharacteristicsFoundState] = @@ -115,7 +115,7 @@ export function DevicenRFTestScreen(_props: DevicenRFTestScreenProps) { } const onDeviceFound = (device: Device) => { - if (device.name?.toLocaleLowerCase() === expectedDeviceName.toLocaleLowerCase()) { + if (device.name?.toLocaleLowerCase() === expectedDeviceName?.toLocaleLowerCase()) { setTestScanDevicesState('DONE') startConnectToDevice(device) .then(onDeviceDisconnected) @@ -593,7 +593,7 @@ export function DevicenRFTestScreen(_props: DevicenRFTestScreenProps) { new Promise((resolve, reject) => { BLEService.scanDevices( (device: Device) => { - if (device.name?.toLocaleLowerCase() === expectedDeviceName.toLocaleLowerCase()) { + if (device.name?.toLocaleLowerCase() === expectedDeviceName?.toLocaleLowerCase()) { BLEService.connectToDevice(device.id) .then(() => BLEService.cancelDeviceConnection()) .then(() => resolve()) diff --git a/example/src/services/BLEService/BLEService.ts b/example/src/services/BLEService/BLEService.ts index 8b6e2a13..d09e3a62 100644 --- a/example/src/services/BLEService/BLEService.ts +++ b/example/src/services/BLEService/BLEService.ts @@ -116,11 +116,15 @@ class BLEServiceInstance { .catch(console.error) } - connectToDevice = (deviceId: DeviceId) => + stopDeviceScan = () => { + this.manager.stopDeviceScan() + } + + connectToDevice = (deviceId: DeviceId, timeout?: number) => new Promise((resolve, reject) => { this.manager.stopDeviceScan() this.manager - .connectToDevice(deviceId) + .connectToDevice(deviceId, { timeout }) .then(device => { this.device = device resolve(device) diff --git a/example/src/services/index.ts b/example/src/services/index.ts index a4a07417..c927d78d 100644 --- a/example/src/services/index.ts +++ b/example/src/services/index.ts @@ -1 +1,3 @@ export * from './BLEService/BLEService' +export * from './storage/persistentDeviceName' +export * from './storage/storage' diff --git a/example/src/services/storage/persistentDeviceName.ts b/example/src/services/storage/persistentDeviceName.ts new file mode 100644 index 00000000..dc6e64f9 --- /dev/null +++ b/example/src/services/storage/persistentDeviceName.ts @@ -0,0 +1,5 @@ +import { useMMKVString } from 'react-native-mmkv' + +export const PERSISTENT_DEVICE_NAME_KEY = 'PERSISTENT_DEVICE_NAME' + +export const usePersistentDeviceName = () => useMMKVString(PERSISTENT_DEVICE_NAME_KEY) diff --git a/example/src/services/storage/storage.ts b/example/src/services/storage/storage.ts new file mode 100644 index 00000000..05466c92 --- /dev/null +++ b/example/src/services/storage/storage.ts @@ -0,0 +1,3 @@ +import { MMKV } from 'react-native-mmkv' + +export const storage = new MMKV() diff --git a/example/yarn.lock b/example/yarn.lock index b78d593d..1b2481d6 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -4764,6 +4764,11 @@ react-native-base64@^0.2.1: resolved "https://registry.yarnpkg.com/react-native-base64/-/react-native-base64-0.2.1.tgz#3d0e73a649c4c0129f7b7695d3912456aebae847" integrity sha512-eHgt/MA8y5ZF0aHfZ1aTPcIkDWxza9AaEk4GcpIX+ZYfZ04RcaNahO+527KR7J44/mD3efYfM23O2C1N44ByWA== +react-native-mmkv@^2.12.2: + version "2.12.2" + resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz#4bba0f5f04e2cf222494cce3a9794ba6a4894dee" + integrity sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg== + react-native-safe-area-context@^4.10.1: version "4.10.1" resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.10.1.tgz#29fb27395ff7dfa2fa38788a27226330d73a81cc" From 149e84cafe7277ad6be978eaf523c700a490046f Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski Date: Wed, 31 Jul 2024 22:26:42 +0200 Subject: [PATCH 4/9] Squashed merge: fix services not available after discoverAllServicesAndCharacteristics --- .../src/main/java/com/bleplx/adapter/Device.java | 15 +++++++++++++++ .../converter/DeviceToJsObjectConverter.java | 14 +++++++++++++- .../com/bleplx/utils/ReadableArrayConverter.java | 15 +++++++++++++++ ios/MultiplatformBleAdapter/BleExtensions.swift | 4 ++-- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/bleplx/adapter/Device.java b/android/src/main/java/com/bleplx/adapter/Device.java index 178709e0..253042be 100755 --- a/android/src/main/java/com/bleplx/adapter/Device.java +++ b/android/src/main/java/com/bleplx/adapter/Device.java @@ -3,6 +3,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -61,6 +62,20 @@ public List getServices() { return services; } + @Nullable + public List getServicesUUIDs() { + if (services == null) { + return null; + } + + List servicesUUIDs = new ArrayList<>(); + for (Service service : services) { + servicesUUIDs.add(service.getUuid()); + } + + return servicesUUIDs; + } + public void setServices(@Nullable List services) { this.services = services; } diff --git a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java index 735be0ec..1c5acdb6 100644 --- a/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java +++ b/android/src/main/java/com/bleplx/converter/DeviceToJsObjectConverter.java @@ -1,9 +1,16 @@ package com.bleplx.converter; +import android.util.Log; + import com.bleplx.adapter.Device; +import com.bleplx.utils.ReadableArrayConverter; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableMap; +import java.util.List; +import java.util.UUID; + public class DeviceToJsObjectConverter extends JSObjectConverter { private interface Metadata { @@ -39,10 +46,15 @@ public WritableMap toJSObject(Device value) { result.putNull(Metadata.MTU); } + if(value.getServices() != null) { + result.putArray(Metadata.SERVICE_UUIDS, ReadableArrayConverter.toReadableArray(value.getServicesUUIDs())); + } else { + result.putNull(Metadata.SERVICE_UUIDS); + } + // 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); diff --git a/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java b/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java index b6772414..2aa5987d 100644 --- a/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java +++ b/android/src/main/java/com/bleplx/utils/ReadableArrayConverter.java @@ -1,6 +1,11 @@ package com.bleplx.utils; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.WritableArray; + +import java.util.List; +import java.util.UUID; public class ReadableArrayConverter { public static String[] toStringArray(ReadableArray readableArray) { @@ -10,4 +15,14 @@ public static String[] toStringArray(ReadableArray readableArray) { } return stringArray; } + + public static ReadableArray toReadableArray(List uuids) { + WritableArray array = Arguments.createArray(); + + for (UUID uuid : uuids) { + array.pushString(uuid.toString()); + } + + return array; + } } diff --git a/ios/MultiplatformBleAdapter/BleExtensions.swift b/ios/MultiplatformBleAdapter/BleExtensions.swift index 0e094c80..20c1832a 100644 --- a/ios/MultiplatformBleAdapter/BleExtensions.swift +++ b/ios/MultiplatformBleAdapter/BleExtensions.swift @@ -104,7 +104,7 @@ extension Peripheral { "manufacturerData": NSNull(), "serviceData": NSNull(), - "serviceUUIDs": NSNull(), + "serviceUUIDs": services?.map { $0.uuid.fullUUIDString } ?? NSNull(), "localName": NSNull(), "txPowerLevel": NSNull(), "solicitedServiceUUIDs": NSNull(), @@ -233,4 +233,4 @@ extension BluetoothState { case .poweredOn: return "PoweredOn" } } -} \ No newline at end of file +} From f0d2e0f281190822965e0d293f358bc734915662 Mon Sep 17 00:00:00 2001 From: intent-kacper-cyranowski Date: Wed, 31 Jul 2024 23:50:54 +0200 Subject: [PATCH 5/9] chore: add changelog entry and update documentation --- CHANGELOG.md | 12 ++++++++++++ docs/GETTING_STARTED.md | 13 +++++++------ docs/index.html | 29 ++++++++++++++++------------- package.json | 2 +- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a49e79..e7e3dc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [3.3.0] - XXXX-XX-XX + +### Changed + +- internal `_manager` property isn't enumerable anymore. This change will hide it from the `console.log`, `JSON.stringify` and other similar methods. +- `BleManager` is now a singleton. It will be created only once and reused across the app. This change will allow users to declare instance in React tree (hooks and components). This change should not affect the existing codebase, where `BleManager` is created once and used across the app. + +### Fixed + +- Timeout parameter in connect method on Android causing the connection to be closed after the timeout period even if connection was established. +- Missing `serviceUUIDs` data after `discoverAllServicesAndCharacteristics` method call + ## [3.2.1] - 2024-07-9 ### Changed diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 63c0f553..2d5e4277 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -2,8 +2,11 @@ react-native-ble-plx -This guide is an introduction to BLE stack and APIs exported by this library. All examples -will be based on CC2541 SensorTag. +This guide is an introduction to BLE stack and APIs exported by this library. For further information you can refer to + +- tutorials and API reference available in this documentation, +- [GitHub wiki](https://github.com/dotintent/react-native-ble-plx/wiki), +- example app available in the repository. ### Install and prepare package @@ -15,7 +18,7 @@ In the case of react native CLI you need to configure two environments: ### Creating BLE Manager -First step is to create BleManager instance which is an entry point to all available APIs. It should be declared **OUTSIDE the life cycle of React**. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2). +First step is to create BleManager instance which is an entry point to all available APIs. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2). #### Ex.1 @@ -42,9 +45,7 @@ import { BleManager } from 'react-native-ble-plx' export const manager = new BleManager() ``` -Only _one_ instance of BleManager is allowed. When you don't need any BLE functionality you can destroy created instance by calling `manager.destroy()` function. You can then recreate `BleManager` later on. - -Note that you may experience undefined behavior when calling a function on one `BleManager` and continuing with another instance. A frequently made error is to create a new instance of the manager for every re-render of a React Native Component. +When you don't need any BLE functionality you can destroy created instance by calling `manager.destroy()` function. You can then recreate `BleManager` later on. ### Ask for permissions diff --git a/docs/index.html b/docs/index.html index 5135673a..f33f4ade 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1975,8 +1975,12 @@

react-native-ble-plx

-

This guide is an introduction to BLE stack and APIs exported by this library. All examples -will be based on CC2541 SensorTag.

+

This guide is an introduction to BLE stack and APIs exported by this library. For further information you can refer to

+
    +
  • tutorials and API reference available in this documentation,
  • +
  • GitHub wiki,
  • +
  • example app available in the repository.
  • +

Install and prepare package

In the case of Expo, you will need to prepare a plugin config, detailed information can be found here: https://github.com/dotintent/react-native-ble-plx?tab=readme-ov-file#expo-sdk-43 In the case of react native CLI you need to configure two environments:

@@ -1985,7 +1989,7 @@

Install and prepare package

  • Android
  • Creating BLE Manager

    -

    First step is to create BleManager instance which is an entry point to all available APIs. It should be declared OUTSIDE the life cycle of React. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2).

    +

    First step is to create BleManager instance which is an entry point to all available APIs. Make sure to create it after application started its execution. We can keep it as a static reference by either creating our own abstraction (ex.1) or by simply creating a new instance (ex.2).

    Ex.1

    import { BleManager } from 'react-native-ble-plx'
     
    @@ -2003,8 +2007,7 @@ 

    Ex.2

    import { BleManager } from 'react-native-ble-plx'
     
     export const manager = new BleManager()
    -

    Only one instance of BleManager is allowed. When you don't need any BLE functionality you can destroy created instance by calling manager.destroy() function. You can then recreate BleManager later on.

    -

    Note that you may experience undefined behavior when calling a function on one BleManager and continuing with another instance. A frequently made error is to create a new instance of the manager for every re-render of a React Native Component.

    +

    When you don't need any BLE functionality you can destroy created instance by calling manager.destroy() function. You can then recreate BleManager later on.

    Ask for permissions

    Check if you requested following permissions