From c4b44bbe3b292c334c6c320365a530f1d89628e0 Mon Sep 17 00:00:00 2001 From: ByteZhang Date: Wed, 9 Apr 2025 12:46:25 +0800 Subject: [PATCH] fix: gitignore --- .gitignore | 2 +- .../so/onekey/lib/ble/utils/BleUtilsModule.kt | 216 ++++++++++++++++++ .../onekey/lib/ble/utils/BleUtilsPackage.kt | 17 ++ .../onekey/lib/ble/utils/data/Peripheral.kt | 35 +++ package.json | 18 +- tsconfig.build.json | 2 +- 6 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 android/src/main/java/so/onekey/lib/ble/utils/BleUtilsModule.kt create mode 100644 android/src/main/java/so/onekey/lib/ble/utils/BleUtilsPackage.kt create mode 100644 android/src/main/java/so/onekey/lib/ble/utils/data/Peripheral.kt diff --git a/.gitignore b/.gitignore index 67f3212..f1be469 100644 --- a/.gitignore +++ b/.gitignore @@ -76,7 +76,7 @@ android/keystores/debug.keystore .turbo/ # generated by bob -lib/ +dist/ # React Native Codegen ios/generated diff --git a/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsModule.kt b/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsModule.kt new file mode 100644 index 0000000..ad70587 --- /dev/null +++ b/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsModule.kt @@ -0,0 +1,216 @@ +package so.onekey.lib.ble.utils + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile.GATT +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.facebook.react.bridge.* +import com.facebook.react.modules.core.DeviceEventManagerModule +import so.onekey.lib.ble.utils.data.Peripheral + + +class BleUtilsModule(private val reactContext: ReactApplicationContext) : + ReactContextBaseJavaModule(reactContext) { + + private var bluetoothManager: BluetoothManager? = null + private val mBleBroadcastReceiver = lazy { + MyBroadcastReceiver(this) + } + + override fun getName() = NAME + + private fun getBluetoothManager(): BluetoothManager? { + if (bluetoothManager == null) { + bluetoothManager = + reactContext.getSystemService(Context.BLUETOOTH_SERVICE) + } + return bluetoothManager + } + + private fun getBluetoothAdapter(): BluetoothAdapter? { + return getBluetoothManager()?.adapter + } + + init { + registerBluetoothReceiver() + } + + private fun registerBluetoothReceiver() { + if (getBluetoothAdapter() == null) { + Log.d(LOG_TAG, "No bluetooth support") + return + } + + val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED) + val intentFilter = IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST) + intentFilter.priority = IntentFilter.SYSTEM_HIGH_PRIORITY + if (Build.VERSION.SDK_INT >= 34) { + // Google in 2023 decides that flag RECEIVER_NOT_EXPORTED or RECEIVER_EXPORTED should be explicit set SDK 34(UPSIDE_DOWN_CAKE) on registering receivers. + // 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. + reactContext.registerReceiver(mBleBroadcastReceiver.value, filter, Context.RECEIVER_EXPORTED) + reactContext.registerReceiver( + mBleBroadcastReceiver.value, + intentFilter, + Context.RECEIVER_EXPORTED + ) + } else { + reactContext.registerReceiver(mBleBroadcastReceiver.value, filter) + reactContext.registerReceiver(mBleBroadcastReceiver.value, intentFilter) + } + Log.d(LOG_TAG, "BleManager initialized") + } + + // 向JS端发送设备绑定状态变化事件 + fun emitOnDeviceBondState(params: WritableMap) { + reactContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + .emit("onDeviceBondState", params) + } + + // 事件监听管理 + @ReactMethod + fun addListener(eventName: String) { + // 实现为空,仅用于满足RN事件监听器注册需求 + Log.d(LOG_TAG, "addListener $eventName") + } + + @ReactMethod + fun removeListeners(count: Int) { + // 实现为空,仅用于满足RN事件监听器注册需求 + Log.d(LOG_TAG, "removeListeners $count") + } + + @ReactMethod + fun checkState(callback: Callback) { + Log.d(LOG_TAG, "checkState") + + val adapter = getBluetoothAdapter() + var state = "off" + if (!reactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + state = "unsupported" + } else if (adapter != null) { + when (adapter.state) { + BluetoothAdapter.STATE_ON -> state = "on" + BluetoothAdapter.STATE_TURNING_ON -> state = "turning_on" + BluetoothAdapter.STATE_TURNING_OFF -> { + state = "turning_off" + } + + BluetoothAdapter.STATE_OFF -> { + // should not happen as per https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getState() + state = "off" + } + + else -> { + state = "off" + } + } + } + + val map: WritableMap = Arguments.createMap() + map.putString("state", state) + Log.d(LOG_TAG, "state:$state") + callback.invoke(state) + } + + @SuppressLint("MissingPermission") + @ReactMethod + fun getBondedPeripherals(callback: Callback) { + val map: WritableArray = Arguments.createArray() + val deviceSet: Set = getBluetoothAdapter()?.getBondedDevices() ?: emptySet() + for (device in deviceSet) { + val peripheral = Peripheral(device) + val jsonBundle: WritableMap = peripheral.asWritableMap() + map.pushMap(jsonBundle) + } + callback.invoke(null, map) + } + + @SuppressLint("MissingPermission") + @ReactMethod + fun getConnectedPeripherals(serviceUUIDs: ReadableArray?, callback: Callback) { + Log.d(LOG_TAG, "Get connected peripherals") + val map: WritableArray = Arguments.createArray() + + if (getBluetoothAdapter() == null) { + Log.d(LOG_TAG, "No bluetooth support") + callback.invoke("No bluetooth support") + return + } + + val peripherals: List = + getBluetoothManager()?.getConnectedDevices(GATT) ?: emptyList() + for (entry in peripherals) { + val peripheral = Peripheral(entry) + val jsonBundle: WritableMap = peripheral.asWritableMap() + map.pushMap(jsonBundle) + } + callback.invoke(null, map) + } + + private class MyBroadcastReceiver(private val module: BleUtilsModule) : BroadcastReceiver() { + @SuppressLint("MissingPermission") + override fun onReceive(context: Context, intent: Intent) { + Log.d(LOG_TAG, "onReceive") + val action = intent.action + + if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { + val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR) + val prevState = intent.getIntExtra( + BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR + ) + val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java + ) + } else { + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + } + + var bondStateStr = "UNKNOWN" + when (bondState) { + BluetoothDevice.BOND_BONDED -> bondStateStr = "BOND_BONDED" + BluetoothDevice.BOND_BONDING -> bondStateStr = "BOND_BONDING" + BluetoothDevice.BOND_NONE -> bondStateStr = "BOND_NONE" + BluetoothDevice.ERROR -> bondStateStr = "BOND_ERROR" + } + + var prevBondStateStr = "UNKNOWN" + when (prevState) { + BluetoothDevice.BOND_BONDED -> prevBondStateStr = "BOND_BONDED" + BluetoothDevice.BOND_BONDING -> prevBondStateStr = "BOND_BONDING" + BluetoothDevice.BOND_NONE -> prevBondStateStr = "BOND_NONE" + BluetoothDevice.ERROR -> bondStateStr = "BOND_ERROR" + } + Log.d(LOG_TAG, "bond state: $bondStateStr") + Log.d(LOG_TAG, "bond state: $prevBondStateStr") + + val bond = Arguments.createMap() + bond.putString("state", bondStateStr) + bond.putString("preState", prevBondStateStr) + + val peripheral = Peripheral(device!!) + val map = peripheral.asWritableMap() + map.putMap("bondState", bond) + Log.d(LOG_TAG, "onReceive BluetoothDevice BondState Change ${map}") + module.emitOnDeviceBondState(map) + } + } + } + + companion object { + const val NAME = "BleUtilsModule" + const val LOG_TAG: String = "RNBleUtils" + } +} diff --git a/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsPackage.kt b/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsPackage.kt new file mode 100644 index 0000000..7b83392 --- /dev/null +++ b/android/src/main/java/so/onekey/lib/ble/utils/BleUtilsPackage.kt @@ -0,0 +1,17 @@ +package so.onekey.lib.ble.utils + +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager + + +class BleUtilsPackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List { + return listOf(BleUtilsModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/android/src/main/java/so/onekey/lib/ble/utils/data/Peripheral.kt b/android/src/main/java/so/onekey/lib/ble/utils/data/Peripheral.kt new file mode 100644 index 0000000..5823740 --- /dev/null +++ b/android/src/main/java/so/onekey/lib/ble/utils/data/Peripheral.kt @@ -0,0 +1,35 @@ +package so.onekey.lib.ble.utils.data + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothDevice +import android.util.Log +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap + + +class Peripheral( + private val device: BluetoothDevice +) { + @SuppressLint("MissingPermission") + fun asWritableMap(): WritableMap { + val map: WritableMap = Arguments.createMap() + val advertising: WritableMap = Arguments.createMap() + + try { + map.putString("name", device.getName()) + map.putString("id", device.getAddress()) // mac address + + val name: String = device.getName() + if (name != null) advertising.putString("localName", name) + + // No scanResult to access so we can't check if peripheral is connectable + advertising.putBoolean("isConnectable", true) + + map.putMap("advertising", advertising) + } catch (e: Exception) { // this shouldn't happen + Log.e("BleUtils", "Unexpected error on asWritableMap", e) + } + + return map + } +} diff --git a/package.json b/package.json index 710b7ed..2f2dcca 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "@onekeyfe/react-native-ble-utils", - "version": "0.1.0", + "version": "0.1.1", "description": "ble uilts", "source": "./src/index.tsx", - "main": "./lib/commonjs/index.js", - "module": "./lib/module/index.js", + "main": "./dist/commonjs/index.js", + "module": "./dist/module/index.js", "react-native": "src/index.ts", - "types": "./lib/typescript/module/src/index.d.ts", + "types": "./dist/typescript/module/src/index.d.ts", "files": [ "src", - "lib", + "dist", "android", "ios", "cpp", @@ -31,7 +31,7 @@ "test": "jest", "typecheck": "tsc", "lint": "eslint \"**/*.{js,ts,tsx}\"", - "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", + "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build dist", "prepare": "bob build", "release": "release-it" }, @@ -90,7 +90,7 @@ "preset": "react-native", "modulePathIgnorePatterns": [ "/example/node_modules", - "/lib/" + "/dist/" ] }, "commitlint": { @@ -137,7 +137,7 @@ }, "eslintIgnore": [ "node_modules/", - "lib/" + "dist/" ], "prettier": { "quoteProps": "consistent", @@ -148,7 +148,7 @@ }, "react-native-builder-bob": { "source": "src", - "output": "lib", + "output": "dist", "targets": [ [ "commonjs", diff --git a/tsconfig.build.json b/tsconfig.build.json index 3c0636a..ba6920a 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "exclude": ["example", "dist"] }