@onekeyfe/react-native-ble-utils 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,216 @@
1
+ package so.onekey.lib.ble.utils
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.bluetooth.BluetoothAdapter
5
+ import android.bluetooth.BluetoothDevice
6
+ import android.bluetooth.BluetoothManager
7
+ import android.bluetooth.BluetoothProfile.GATT
8
+ import android.content.BroadcastReceiver
9
+ import android.content.Context
10
+ import android.content.Intent
11
+ import android.content.IntentFilter
12
+ import android.content.pm.PackageManager
13
+ import android.os.Build
14
+ import android.os.Handler
15
+ import android.os.Looper
16
+ import android.util.Log
17
+ import com.facebook.react.bridge.*
18
+ import com.facebook.react.modules.core.DeviceEventManagerModule
19
+ import so.onekey.lib.ble.utils.data.Peripheral
20
+
21
+
22
+ class BleUtilsModule(private val reactContext: ReactApplicationContext) :
23
+ ReactContextBaseJavaModule(reactContext) {
24
+
25
+ private var bluetoothManager: BluetoothManager? = null
26
+ private val mBleBroadcastReceiver = lazy {
27
+ MyBroadcastReceiver(this)
28
+ }
29
+
30
+ override fun getName() = NAME
31
+
32
+ private fun getBluetoothManager(): BluetoothManager? {
33
+ if (bluetoothManager == null) {
34
+ bluetoothManager =
35
+ reactContext.getSystemService(Context.BLUETOOTH_SERVICE)
36
+ }
37
+ return bluetoothManager
38
+ }
39
+
40
+ private fun getBluetoothAdapter(): BluetoothAdapter? {
41
+ return getBluetoothManager()?.adapter
42
+ }
43
+
44
+ init {
45
+ registerBluetoothReceiver()
46
+ }
47
+
48
+ private fun registerBluetoothReceiver() {
49
+ if (getBluetoothAdapter() == null) {
50
+ Log.d(LOG_TAG, "No bluetooth support")
51
+ return
52
+ }
53
+
54
+ val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
55
+ filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
56
+ val intentFilter = IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)
57
+ intentFilter.priority = IntentFilter.SYSTEM_HIGH_PRIORITY
58
+ if (Build.VERSION.SDK_INT >= 34) {
59
+ // Google in 2023 decides that flag RECEIVER_NOT_EXPORTED or RECEIVER_EXPORTED should be explicit set SDK 34(UPSIDE_DOWN_CAKE) on registering receivers.
60
+ // Also the export flags are available on Android 8 and higher, should be used with caution so that don't break compability with that devices.
61
+ reactContext.registerReceiver(mBleBroadcastReceiver.value, filter, Context.RECEIVER_EXPORTED)
62
+ reactContext.registerReceiver(
63
+ mBleBroadcastReceiver.value,
64
+ intentFilter,
65
+ Context.RECEIVER_EXPORTED
66
+ )
67
+ } else {
68
+ reactContext.registerReceiver(mBleBroadcastReceiver.value, filter)
69
+ reactContext.registerReceiver(mBleBroadcastReceiver.value, intentFilter)
70
+ }
71
+ Log.d(LOG_TAG, "BleManager initialized")
72
+ }
73
+
74
+ // 向JS端发送设备绑定状态变化事件
75
+ fun emitOnDeviceBondState(params: WritableMap) {
76
+ reactContext
77
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
78
+ .emit("onDeviceBondState", params)
79
+ }
80
+
81
+ // 事件监听管理
82
+ @ReactMethod
83
+ fun addListener(eventName: String) {
84
+ // 实现为空,仅用于满足RN事件监听器注册需求
85
+ Log.d(LOG_TAG, "addListener $eventName")
86
+ }
87
+
88
+ @ReactMethod
89
+ fun removeListeners(count: Int) {
90
+ // 实现为空,仅用于满足RN事件监听器注册需求
91
+ Log.d(LOG_TAG, "removeListeners $count")
92
+ }
93
+
94
+ @ReactMethod
95
+ fun checkState(callback: Callback) {
96
+ Log.d(LOG_TAG, "checkState")
97
+
98
+ val adapter = getBluetoothAdapter()
99
+ var state = "off"
100
+ if (!reactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
101
+ state = "unsupported"
102
+ } else if (adapter != null) {
103
+ when (adapter.state) {
104
+ BluetoothAdapter.STATE_ON -> state = "on"
105
+ BluetoothAdapter.STATE_TURNING_ON -> state = "turning_on"
106
+ BluetoothAdapter.STATE_TURNING_OFF -> {
107
+ state = "turning_off"
108
+ }
109
+
110
+ BluetoothAdapter.STATE_OFF -> {
111
+ // should not happen as per https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getState()
112
+ state = "off"
113
+ }
114
+
115
+ else -> {
116
+ state = "off"
117
+ }
118
+ }
119
+ }
120
+
121
+ val map: WritableMap = Arguments.createMap()
122
+ map.putString("state", state)
123
+ Log.d(LOG_TAG, "state:$state")
124
+ callback.invoke(state)
125
+ }
126
+
127
+ @SuppressLint("MissingPermission")
128
+ @ReactMethod
129
+ fun getBondedPeripherals(callback: Callback) {
130
+ val map: WritableArray = Arguments.createArray()
131
+ val deviceSet: Set<BluetoothDevice> = getBluetoothAdapter()?.getBondedDevices() ?: emptySet()
132
+ for (device in deviceSet) {
133
+ val peripheral = Peripheral(device)
134
+ val jsonBundle: WritableMap = peripheral.asWritableMap()
135
+ map.pushMap(jsonBundle)
136
+ }
137
+ callback.invoke(null, map)
138
+ }
139
+
140
+ @SuppressLint("MissingPermission")
141
+ @ReactMethod
142
+ fun getConnectedPeripherals(serviceUUIDs: ReadableArray?, callback: Callback) {
143
+ Log.d(LOG_TAG, "Get connected peripherals")
144
+ val map: WritableArray = Arguments.createArray()
145
+
146
+ if (getBluetoothAdapter() == null) {
147
+ Log.d(LOG_TAG, "No bluetooth support")
148
+ callback.invoke("No bluetooth support")
149
+ return
150
+ }
151
+
152
+ val peripherals: List<BluetoothDevice> =
153
+ getBluetoothManager()?.getConnectedDevices(GATT) ?: emptyList()
154
+ for (entry in peripherals) {
155
+ val peripheral = Peripheral(entry)
156
+ val jsonBundle: WritableMap = peripheral.asWritableMap()
157
+ map.pushMap(jsonBundle)
158
+ }
159
+ callback.invoke(null, map)
160
+ }
161
+
162
+ private class MyBroadcastReceiver(private val module: BleUtilsModule) : BroadcastReceiver() {
163
+ @SuppressLint("MissingPermission")
164
+ override fun onReceive(context: Context, intent: Intent) {
165
+ Log.d(LOG_TAG, "onReceive")
166
+ val action = intent.action
167
+
168
+ if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
169
+ val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)
170
+ val prevState = intent.getIntExtra(
171
+ BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR
172
+ )
173
+ val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
174
+ intent.getParcelableExtra(
175
+ BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java
176
+ )
177
+ } else {
178
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
179
+ }
180
+
181
+ var bondStateStr = "UNKNOWN"
182
+ when (bondState) {
183
+ BluetoothDevice.BOND_BONDED -> bondStateStr = "BOND_BONDED"
184
+ BluetoothDevice.BOND_BONDING -> bondStateStr = "BOND_BONDING"
185
+ BluetoothDevice.BOND_NONE -> bondStateStr = "BOND_NONE"
186
+ BluetoothDevice.ERROR -> bondStateStr = "BOND_ERROR"
187
+ }
188
+
189
+ var prevBondStateStr = "UNKNOWN"
190
+ when (prevState) {
191
+ BluetoothDevice.BOND_BONDED -> prevBondStateStr = "BOND_BONDED"
192
+ BluetoothDevice.BOND_BONDING -> prevBondStateStr = "BOND_BONDING"
193
+ BluetoothDevice.BOND_NONE -> prevBondStateStr = "BOND_NONE"
194
+ BluetoothDevice.ERROR -> bondStateStr = "BOND_ERROR"
195
+ }
196
+ Log.d(LOG_TAG, "bond state: $bondStateStr")
197
+ Log.d(LOG_TAG, "bond state: $prevBondStateStr")
198
+
199
+ val bond = Arguments.createMap()
200
+ bond.putString("state", bondStateStr)
201
+ bond.putString("preState", prevBondStateStr)
202
+
203
+ val peripheral = Peripheral(device!!)
204
+ val map = peripheral.asWritableMap()
205
+ map.putMap("bondState", bond)
206
+ Log.d(LOG_TAG, "onReceive BluetoothDevice BondState Change ${map}")
207
+ module.emitOnDeviceBondState(map)
208
+ }
209
+ }
210
+ }
211
+
212
+ companion object {
213
+ const val NAME = "BleUtilsModule"
214
+ const val LOG_TAG: String = "RNBleUtils"
215
+ }
216
+ }
@@ -0,0 +1,17 @@
1
+ package so.onekey.lib.ble.utils
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+
9
+ class BleUtilsPackage : ReactPackage {
10
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
+ return listOf(BleUtilsModule(reactContext))
12
+ }
13
+
14
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
+ return emptyList()
16
+ }
17
+ }
@@ -0,0 +1,35 @@
1
+ package so.onekey.lib.ble.utils.data
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.bluetooth.BluetoothDevice
5
+ import android.util.Log
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.WritableMap
8
+
9
+
10
+ class Peripheral(
11
+ private val device: BluetoothDevice
12
+ ) {
13
+ @SuppressLint("MissingPermission")
14
+ fun asWritableMap(): WritableMap {
15
+ val map: WritableMap = Arguments.createMap()
16
+ val advertising: WritableMap = Arguments.createMap()
17
+
18
+ try {
19
+ map.putString("name", device.getName())
20
+ map.putString("id", device.getAddress()) // mac address
21
+
22
+ val name: String = device.getName()
23
+ if (name != null) advertising.putString("localName", name)
24
+
25
+ // No scanResult to access so we can't check if peripheral is connectable
26
+ advertising.putBoolean("isConnectable", true)
27
+
28
+ map.putMap("advertising", advertising)
29
+ } catch (e: Exception) { // this shouldn't happen
30
+ Log.e("BleUtils", "Unexpected error on asWritableMap", e)
31
+ }
32
+
33
+ return map
34
+ }
35
+ }
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-ble-utils",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "ble uilts",
5
5
  "source": "./src/index.tsx",
6
- "main": "./lib/commonjs/index.js",
7
- "module": "./lib/module/index.js",
6
+ "main": "./dist/commonjs/index.js",
7
+ "module": "./dist/module/index.js",
8
8
  "react-native": "src/index.ts",
9
- "types": "./lib/typescript/module/src/index.d.ts",
9
+ "types": "./dist/typescript/module/src/index.d.ts",
10
10
  "files": [
11
11
  "src",
12
- "lib",
12
+ "dist",
13
13
  "android",
14
14
  "ios",
15
15
  "cpp",
@@ -31,7 +31,7 @@
31
31
  "test": "jest",
32
32
  "typecheck": "tsc",
33
33
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
34
- "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
34
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build dist",
35
35
  "prepare": "bob build",
36
36
  "release": "release-it"
37
37
  },
@@ -90,7 +90,7 @@
90
90
  "preset": "react-native",
91
91
  "modulePathIgnorePatterns": [
92
92
  "<rootDir>/example/node_modules",
93
- "<rootDir>/lib/"
93
+ "<rootDir>/dist/"
94
94
  ]
95
95
  },
96
96
  "commitlint": {
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "eslintIgnore": [
139
139
  "node_modules/",
140
- "lib/"
140
+ "dist/"
141
141
  ],
142
142
  "prettier": {
143
143
  "quoteProps": "consistent",
@@ -148,7 +148,7 @@
148
148
  },
149
149
  "react-native-builder-bob": {
150
150
  "source": "src",
151
- "output": "lib",
151
+ "output": "dist",
152
152
  "targets": [
153
153
  [
154
154
  "commonjs",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes