From 08b8495b3d86c81cc7675e91b11dfd5dc665c77a Mon Sep 17 00:00:00 2001 From: km2025 Date: Thu, 8 Jan 2026 20:42:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=E5=B9=B6=E5=9C=A8=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=B8=AD=E6=98=BE=E7=A4=BA=EF=BC=9B=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=20BLE=20=E8=BF=9E=E6=8E=A5=E5=92=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.constants.js | 2 +- app/(tabs)/index.tsx | 2 +- app/auth.tsx | 2 + ble/core/BleClient.ts | 104 +++++++++++---- ble/services/BleProtocolService.ts | 95 ++++++++++---- bun.lock | 35 ++++- package.json | 2 + patches/react-native-ble-plx+3.5.0.patch | 157 +++++++++++++++++++++++ scripts/patch-ble-plx.js | 29 +++++ 9 files changed, 370 insertions(+), 58 deletions(-) create mode 100644 patches/react-native-ble-plx+3.5.0.patch create mode 100644 scripts/patch-ble-plx.js diff --git a/app.constants.js b/app.constants.js index d8e8ec7..b46c19a 100644 --- a/app.constants.js +++ b/app.constants.js @@ -8,6 +8,6 @@ export const ANDROID_ID = 'com.duomi.duooomi' export const IOS_ID = ANDROID_ID export const IOS_UNIVERSAL_LINK = 'duooomi.bowong.cn' export const OWNER_ID = 'x3xbTCWf7dbtWu4gGU2TeI054L77xtkt' -export const APP_VERSION = 'dev202601081220' +export const APP_VERSION = 'dev202601082028' export const ALIPAY_SCHEMA = 'alipay2021006119657394' export const ALIPAY_SCHEMA_SANDBOX = '9021000158673972' diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index a3d0a4c..903104b 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -518,7 +518,7 @@ type HeroCircleProps = { onQuickGen: () => void onOpenSearch: () => void } -const HeroCircle = memo(function HeroCircle({ selectedItem, onQuickGen, onOpenSearch }) { +const HeroCircle = observer(function HeroCircle({ selectedItem, onQuickGen, onOpenSearch }) { const isAuthenticated = userStore.isAuthenticated const { balance } = userBalanceStore const existItem = !!selectedItem?.url diff --git a/app/auth.tsx b/app/auth.tsx index 8d6f2b7..da9980e 100644 --- a/app/auth.tsx +++ b/app/auth.tsx @@ -5,6 +5,7 @@ import React, { useCallback, useState } from 'react' import { TextInput } from 'react-native' import { KeyboardAwareScrollView } from 'react-native-keyboard-controller' +import { APP_VERSION } from '@/app.constants' import BannerSection from '@/components/BannerSection' import { setAuthToken, signIn, signUp } from '@/lib/auth' type AuthMode = 'login' | 'register' @@ -340,6 +341,7 @@ export default function Auth() { © 2025 LOOMART. All rights reserved. + 插件当前版本号: {APP_VERSION} diff --git a/ble/core/BleClient.ts b/ble/core/BleClient.ts index 7c24280..78d00cd 100644 --- a/ble/core/BleClient.ts +++ b/ble/core/BleClient.ts @@ -163,30 +163,43 @@ export class BleClient { if (this.connectedDevice) { console.log(`Disconnecting from ${this.connectedDevice.id}`) - // ✅ 修复:先清理监听器 - this.removeAllListeners() + const deviceId = this.connectedDevice.id - // ✅ 修复:安全断开 - await this.connectedDevice.cancelConnection() + // ✅ 修复:先触发断开状态,然后清理 + this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTING }) + try { + // ✅ 修复:安全断开,设置较短超时 + await Promise.race([ + this.connectedDevice.cancelConnection(), + new Promise((_, reject) => setTimeout(() => reject(new Error('Disconnect timeout')), 5000)), + ]) + } catch (disconnectError: any) { + // 忽略断开连接过程中的预期错误 + const errorMsg = disconnectError?.message || String(disconnectError) + console.debug('Disconnect operation completed with:', errorMsg) + } + + // ✅ 修复:无论断开是否成功,都清理状态 this.connectedDevice = null + this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED }) + console.log('Device disconnected successfully') } } catch (error: any) { const errorMsg = error?.message || String(error) + console.error('Disconnect error:', errorMsg) - // ✅ 修复:忽略预期的断开连接错误 - if ( - errorMsg.includes('already disconnected') || - errorMsg.includes('GATT_SUCCESS') || - errorMsg.includes('Disconnected') || - error.errorCode === 0 - ) { - console.debug('Disconnect completed (expected error):', errorMsg) + // 即使出现错误,也要清理状态 + if (this.connectedDevice) { + const deviceId = this.connectedDevice.id this.connectedDevice = null - } else { - console.error('Disconnect error:', errorMsg) - throw error + this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED }) + } + + // 只在严重错误时抛出异常 + if (!this.isDisconnectionError(error)) { + throw this.normalizeError(error) } } } @@ -239,18 +252,36 @@ export class BleClient { listener: (error: BleError | null, value: string | null) => void, ) { if (!this.manager) { - listener({ message: 'BLE not supported on web', errorCode: 0, reason: null }, null) + listener({ message: 'BLE not supported on web', errorCode: -1, reason: 'platform_not_supported' }, null) return { remove: () => {}, } } - return this.manager.monitorCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID, (error, char) => { - if (error) { - listener(this.normalizeError(error), null) - } else { - listener(null, char?.value || null) + + try { + return this.manager.monitorCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID, (error, char) => { + if (error) { + // ✅ 修复:安全处理错误,确保不传递 null/undefined 值 + const normalizedError = this.normalizeError(error) + + // 特别处理断开连接错误,避免崩溃 + if (this.isDisconnectionError(error)) { + console.debug('Device disconnected during monitoring:', normalizedError.message) + } + + listener(normalizedError, null) + } else { + listener(null, char?.value || null) + } + }) + } catch (error: any) { + // ✅ 修复:捕获监听器设置时的异常 + const normalizedError = this.normalizeError(error) + listener(normalizedError, null) + return { + remove: () => {}, } - }) + } } public async requestMtu(deviceId: string, mtu: number): Promise { @@ -270,11 +301,32 @@ export class BleClient { } private normalizeError(e: any): BleError { - // wrapper to convert PlxError to our BleError + // ✅ 修复:安全处理错误对象,确保所有字段都有有效值 + const errorCode = typeof e?.errorCode === 'number' ? e.errorCode : -1 + const message = + typeof e?.message === 'string' && e.message.trim() + ? e.message + : typeof e === 'string' && e.trim() + ? e + : 'Unknown BLE error' + const reason = typeof e?.reason === 'string' && e.reason.trim() ? e.reason : 'unknown' + return { - errorCode: e.errorCode || 0, - message: e.message || 'Unknown error', - reason: e.reason, + errorCode, + message, + reason, } } + + private isDisconnectionError(error: any): boolean { + const message = error?.message || '' + const reason = error?.reason || '' + + return ( + message.toLowerCase().includes('disconnect') || + message.toLowerCase().includes('gatt_conn_terminate') || + reason.toLowerCase().includes('disconnect') || + error?.errorCode === 19 + ) // GATT_CONN_TERMINATE_PEER_USER + } } diff --git a/ble/services/BleProtocolService.ts b/ble/services/BleProtocolService.ts index ac3fad3..962d0c1 100644 --- a/ble/services/BleProtocolService.ts +++ b/ble/services/BleProtocolService.ts @@ -51,40 +51,55 @@ export class BleProtocolService { this.clearFragments(deviceId) try { + console.log('Initializing BLE protocol service for device:', deviceId) + this.subscription = await this.client.monitor( deviceId, BLE_UUIDS.SERVICE, BLE_UUIDS.READ_CHARACTERISTIC, (error, value) => { if (error) { - // Check for known native crash error and ignore/log as debug - if ( - error.errorCode === 0 && - error.message.includes('Unknown error') && - error.reason?.includes('PromiseImpl.reject') - ) { - console.debug('Ignored native monitor error', error) + // ✅ 修复:增强错误处理,避免崩溃 + const errorMsg = error.message || 'Unknown monitor error' + + // 忽略预期的断开连接错误 + if (this.isExpectedDisconnectionError(error)) { + console.debug('Device disconnected during monitoring:', errorMsg) return } - console.warn('Monitor error', error) + + // 忽略已知的原生崩溃错误 + if (this.isKnownNativeCrashError(error)) { + console.debug('Ignored known native monitor error:', errorMsg) + return + } + + console.warn('BLE monitor error:', errorMsg) return } - if (value) { - const buffer = Buffer.from(value, 'base64') - const hexString = - buffer - .toString('hex') - .match(/.{1,2}/g) - ?.join(' ') - .toUpperCase() || '' - console.log(`[BleProtocol] Received ${buffer.byteLength} bytes:`, hexString) - this.handleRawData(deviceId, buffer) + if (value) { + try { + const buffer = Buffer.from(value, 'base64') + const hexString = + buffer + .toString('hex') + .match(/.{1,2}/g) + ?.join(' ') + .toUpperCase() || '' + + console.log(`[BleProtocol] Received ${buffer.byteLength} bytes:`, hexString) + this.handleRawData(deviceId, buffer) + } catch (parseError: any) { + console.warn('Failed to parse received data:', parseError.message) + } } }, ) - } catch (error) { - console.error('Failed to initialize protocol service:', error) + + console.log('BLE protocol service initialized successfully') + } catch (error: any) { + console.error('Failed to initialize protocol service:', error.message) throw error } } @@ -92,12 +107,22 @@ export class BleProtocolService { public disconnect() { if (this.subscription) { try { + console.log('Cleaning up BLE protocol subscription...') this.subscription.remove() - } catch (e) { - console.warn('Failed to remove subscription', e) + console.log('BLE protocol subscription removed successfully') + } catch (e: any) { + // ✅ 修复:安全处理订阅清理错误,避免崩溃 + const errorMsg = e?.message || String(e) + console.debug('Subscription cleanup completed with warning:', errorMsg) + } finally { + // ✅ 确保订阅引用被清理 + this.subscription = null } - this.subscription = null } + + // ✅ 清理分片缓存 + this.fragments.clear() + console.log('BLE protocol service disconnected') } private handleRawData(deviceId: string, data: Buffer) { @@ -209,4 +234,28 @@ export class BleProtocolService { // console.debug("Wrote frame", result); } } + + private isExpectedDisconnectionError(error: any): boolean { + const message = error?.message?.toLowerCase() || '' + const reason = error?.reason?.toLowerCase() || '' + + return ( + message.includes('disconnect') || + message.includes('gatt_conn_terminate') || + message.includes('connection terminated') || + reason.includes('disconnect') || + error?.errorCode === 19 + ) // GATT_CONN_TERMINATE_PEER_USER + } + + private isKnownNativeCrashError(error: any): boolean { + const message = error?.message || '' + const reason = error?.reason || '' + + return ( + (error.errorCode === 0 && message.includes('Unknown error') && reason?.includes('PromiseImpl.reject')) || + message.includes('Parameter specified as non-null is null') || + reason?.includes('SafePromise.reject') + ) + } } diff --git a/bun.lock b/bun.lock index fa8a022..0996c97 100644 --- a/bun.lock +++ b/bun.lock @@ -105,6 +105,7 @@ "eslint-plugin-tailwindcss": "^3.18.0", "eslint-plugin-unicorn": "^59.0.1", "eslint-plugin-unused-imports": "^4.1.4", + "patch-package": "^8.0.1", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.12", "tailwindcss": "3.4.4", @@ -777,6 +778,8 @@ "@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="], + "@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], @@ -1271,6 +1274,8 @@ "find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="], + "find-yarn-workspace-root": ["find-yarn-workspace-root@2.0.0", "", { "dependencies": { "micromatch": "^4.0.2" } }, "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ=="], + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], @@ -1285,6 +1290,8 @@ "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], + "fs-readdir-recursive": ["fs-readdir-recursive@1.1.0", "", {}, "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -1459,7 +1466,7 @@ "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -1509,16 +1516,24 @@ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="], + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + + "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], "jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="], + "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="], @@ -1735,6 +1750,8 @@ "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -2081,6 +2098,8 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "tmpl": ["tmpl@1.0.5", "", {}, "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -2133,6 +2152,8 @@ "unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="], + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], @@ -2463,6 +2484,10 @@ "ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + "patch-package/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "patch-package/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], @@ -2505,6 +2530,8 @@ "read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + "readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], @@ -2513,10 +2540,6 @@ "requireg/resolve": ["resolve@1.7.1", "", { "dependencies": { "path-parse": "^1.0.5" } }, "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw=="], - "safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - - "safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="], @@ -2545,8 +2568,6 @@ "whatwg-url-without-unicode/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], diff --git a/package.json b/package.json index 1740fed..dd43f8a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "packageManager": "bun@1.3.5", "private": true, "scripts": { + "postinstall": "patch-package", "claude": "claude --dangerously-skip-permissions", "start": "dotenv -e .env.development -- expo start", "start:test": "dotenv -e .env.test -- expo start", @@ -132,6 +133,7 @@ "eslint-plugin-tailwindcss": "^3.18.0", "eslint-plugin-unicorn": "^59.0.1", "eslint-plugin-unused-imports": "^4.1.4", + "patch-package": "^8.0.1", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.12", "tailwindcss": "3.4.4", diff --git a/patches/react-native-ble-plx+3.5.0.patch b/patches/react-native-ble-plx+3.5.0.patch new file mode 100644 index 0000000..f1635e7 --- /dev/null +++ b/patches/react-native-ble-plx+3.5.0.patch @@ -0,0 +1,157 @@ +diff --git a/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java b/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java +index 1234567..abcdefg 100644 +--- a/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java ++++ b/node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java +@@ -168,7 +168,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -187,7 +187,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -318,7 +318,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -338,7 +338,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -358,7 +358,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -422,7 +422,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -442,7 +442,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -483,7 +483,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -627,7 +627,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -654,7 +654,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -680,7 +680,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + }); + } +@@ -706,7 +706,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -732,7 +732,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -757,7 +757,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -788,7 +788,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -818,7 +818,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); +@@ -848,7 +848,7 @@ public class BlePlxModule extends ReactContextBaseJavaModule { + }, new OnErrorCallback() { + @Override + public void onError(BleError error) { +- safePromise.reject(null, errorConverter.toJs(error)); ++ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error)); + } + } + ); \ No newline at end of file diff --git a/scripts/patch-ble-plx.js b/scripts/patch-ble-plx.js new file mode 100644 index 0000000..8dfa314 --- /dev/null +++ b/scripts/patch-ble-plx.js @@ -0,0 +1,29 @@ +const fs = require('fs'); +const path = require('path'); + +const filePath = path.join(__dirname, '..', 'node_modules', 'react-native-ble-plx', 'android', 'src', 'main', 'java', 'com', 'bleplx', 'BlePlxModule.java'); + +console.log('正在修复 react-native-ble-plx 的 Android 崩溃问题...'); + +try { + let content = fs.readFileSync(filePath, 'utf8'); + + // 替换所有的 safePromise.reject(null, errorConverter.toJs(error)) + const originalPattern = 'safePromise.reject(null, errorConverter.toJs(error));'; + const fixedPattern = 'safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));'; + + const beforeCount = (content.match(/safePromise\.reject\(null, errorConverter\.toJs\(error\)\);/g) || []).length; + + content = content.replace(/safePromise\.reject\(null, errorConverter\.toJs\(error\)\);/g, fixedPattern); + + const afterCount = (content.match(/safePromise\.reject\(error\.errorCode\.name\(\), errorConverter\.toJs\(error\)\);/g) || []).length; + + fs.writeFileSync(filePath, content, 'utf8'); + + console.log(`✅ 成功修复 ${beforeCount} 个实例`); + console.log(`✅ 验证:发现 ${afterCount} 个修复后的实例`); + console.log('现在请运行: bunx patch-package react-native-ble-plx'); +} catch (error) { + console.error('❌ 修复失败:', error.message); + process.exit(1); +} \ No newline at end of file