Skip to content

Commit e8326ff

Browse files
committed
fix(android): fixes Java IndexOutOfBoundsException exception
1 parent 7210d9d commit e8326ff

File tree

20 files changed

+189
-72
lines changed

20 files changed

+189
-72
lines changed

apps/expo-example-app/app.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
"updates": {
1414
"fallbackToCacheTimeout": 0
1515
},
16-
"assetBundlePatterns": [
17-
"**/*"
18-
],
16+
"assetBundlePatterns": ["**/*"],
1917
"ios": {
2018
"supportsTablet": true,
21-
"bundleIdentifier": "com.patwoz.expoexampleapp"
19+
"bundleIdentifier": "com.patwoz.expoexampleapp",
20+
"infoPlist": {
21+
"NSBluetoothAlwaysUsageDescription": "Your reason to use bluetooth"
22+
}
2223
},
2324
"android": {
2425
"adaptiveIcon": {

apps/expo-example-app/ios/ExpoExampleApp.xcodeproj/project.pbxproj

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 46;
6+
objectVersion = 54;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -27,7 +27,7 @@
2727
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExpoExampleApp/main.m; sourceTree = "<group>"; };
2828
32B9975796F647FABCCD77EB /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "ExpoExampleApp/noop-file.swift"; sourceTree = "<group>"; };
2929
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-ExpoExampleApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpoExampleApp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
30-
6451BEBE8E39D32B566384D5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = ExpoExampleApp/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
30+
6451BEBE8E39D32B566384D5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ExpoExampleApp/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
3131
6C2E3173556A471DD304B334 /* Pods-ExpoExampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpoExampleApp.debug.xcconfig"; path = "Target Support Files/Pods-ExpoExampleApp/Pods-ExpoExampleApp.debug.xcconfig"; sourceTree = "<group>"; };
3232
7A4D352CD337FB3A3BF06240 /* Pods-ExpoExampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpoExampleApp.release.xcconfig"; path = "Target Support Files/Pods-ExpoExampleApp/Pods-ExpoExampleApp.release.xcconfig"; sourceTree = "<group>"; };
3333
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = ExpoExampleApp/SplashScreen.storyboard; sourceTree = "<group>"; };
@@ -341,14 +341,18 @@
341341
CLANG_ENABLE_MODULES = YES;
342342
CODE_SIGN_ENTITLEMENTS = ExpoExampleApp/ExpoExampleApp.entitlements;
343343
CURRENT_PROJECT_VERSION = 1;
344+
DEVELOPMENT_TEAM = UB663H59T6;
344345
ENABLE_BITCODE = NO;
345346
GCC_PREPROCESSOR_DEFINITIONS = (
346347
"$(inherited)",
347348
"FB_SONARKIT_ENABLED=1",
348349
);
349350
INFOPLIST_FILE = ExpoExampleApp/Info.plist;
350351
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
351-
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
352+
LD_RUNPATH_SEARCH_PATHS = (
353+
"$(inherited)",
354+
"@executable_path/Frameworks",
355+
);
352356
MARKETING_VERSION = 1.0;
353357
OTHER_LDFLAGS = (
354358
"$(inherited)",
@@ -374,9 +378,13 @@
374378
CLANG_ENABLE_MODULES = YES;
375379
CODE_SIGN_ENTITLEMENTS = ExpoExampleApp/ExpoExampleApp.entitlements;
376380
CURRENT_PROJECT_VERSION = 1;
381+
DEVELOPMENT_TEAM = UB663H59T6;
377382
INFOPLIST_FILE = ExpoExampleApp/Info.plist;
378383
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
379-
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
384+
LD_RUNPATH_SEARCH_PATHS = (
385+
"$(inherited)",
386+
"@executable_path/Frameworks",
387+
);
380388
MARKETING_VERSION = 1.0;
381389
OTHER_LDFLAGS = (
382390
"$(inherited)",
@@ -441,14 +449,14 @@
441449
GCC_WARN_UNUSED_FUNCTION = YES;
442450
GCC_WARN_UNUSED_VARIABLE = YES;
443451
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
444-
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
452+
LD_RUNPATH_SEARCH_PATHS = (
453+
/usr/lib/swift,
454+
"$(inherited)",
455+
);
445456
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
446457
MTL_ENABLE_DEBUG_INFO = YES;
447458
ONLY_ACTIVE_ARCH = YES;
448-
OTHER_LDFLAGS = (
449-
"$(inherited)",
450-
" ",
451-
);
459+
OTHER_LDFLAGS = "$(inherited) ";
452460
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
453461
SDKROOT = iphoneos;
454462
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -497,13 +505,13 @@
497505
GCC_WARN_UNUSED_FUNCTION = YES;
498506
GCC_WARN_UNUSED_VARIABLE = YES;
499507
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
500-
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
501-
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
502-
MTL_ENABLE_DEBUG_INFO = NO;
503-
OTHER_LDFLAGS = (
508+
LD_RUNPATH_SEARCH_PATHS = (
509+
/usr/lib/swift,
504510
"$(inherited)",
505-
" ",
506511
);
512+
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
513+
MTL_ENABLE_DEBUG_INFO = NO;
514+
OTHER_LDFLAGS = "$(inherited) ";
507515
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
508516
SDKROOT = iphoneos;
509517
USE_HERMES = true;

apps/expo-example-app/ios/Podfile.lock

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
11
PODS:
2+
- BluetoothStateManager (2.0.1):
3+
- DoubleConversion
4+
- glog
5+
- hermes-engine
6+
- NitroModules
7+
- RCT-Folly (= 2024.11.18.00)
8+
- RCTRequired
9+
- RCTTypeSafety
10+
- React-Core
11+
- React-debug
12+
- React-Fabric
13+
- React-featureflags
14+
- React-graphics
15+
- React-ImageManager
16+
- React-NativeModulesApple
17+
- React-RCTFabric
18+
- React-rendererdebug
19+
- React-utils
20+
- ReactCodegen
21+
- ReactCommon/turbomodule/bridging
22+
- ReactCommon/turbomodule/core
23+
- Yoga
224
- boost (1.84.0)
325
- DoubleConversion (1.1.6)
426
- EXConstants (17.0.8):
@@ -44,6 +66,29 @@ PODS:
4466
- hermes-engine (0.77.0):
4567
- hermes-engine/Pre-built (= 0.77.0)
4668
- hermes-engine/Pre-built (0.77.0)
69+
- NitroModules (0.25.2):
70+
- DoubleConversion
71+
- glog
72+
- hermes-engine
73+
- RCT-Folly (= 2024.11.18.00)
74+
- RCTRequired
75+
- RCTTypeSafety
76+
- React-callinvoker
77+
- React-Core
78+
- React-debug
79+
- React-Fabric
80+
- React-featureflags
81+
- React-graphics
82+
- React-ImageManager
83+
- React-jsi
84+
- React-NativeModulesApple
85+
- React-RCTFabric
86+
- React-rendererdebug
87+
- React-utils
88+
- ReactCodegen
89+
- ReactCommon/turbomodule/bridging
90+
- ReactCommon/turbomodule/core
91+
- Yoga
4792
- RCT-Folly (2024.11.18.00):
4893
- boost
4994
- DoubleConversion
@@ -1539,6 +1584,7 @@ PODS:
15391584
- Yoga (0.0.0)
15401585

15411586
DEPENDENCIES:
1587+
- BluetoothStateManager (from `../../../node_modules/react-native-bluetooth-state-manager`)
15421588
- boost (from `../../../node_modules/react-native/third-party-podspecs/boost.podspec`)
15431589
- DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
15441590
- EXConstants (from `../../../node_modules/expo-constants/ios`)
@@ -1553,6 +1599,7 @@ DEPENDENCIES:
15531599
- fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
15541600
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
15551601
- hermes-engine (from `../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
1602+
- NitroModules (from `../../../node_modules/react-native-nitro-modules`)
15561603
- RCT-Folly (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
15571604
- RCT-Folly/Fabric (from `../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
15581605
- RCTDeprecation (from `../../../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
@@ -1620,6 +1667,8 @@ SPEC REPOS:
16201667
- SocketRocket
16211668

16221669
EXTERNAL SOURCES:
1670+
BluetoothStateManager:
1671+
:path: "../../../node_modules/react-native-bluetooth-state-manager"
16231672
boost:
16241673
:podspec: "../../../node_modules/react-native/third-party-podspecs/boost.podspec"
16251674
DoubleConversion:
@@ -1649,6 +1698,8 @@ EXTERNAL SOURCES:
16491698
hermes-engine:
16501699
:podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
16511700
:tag: hermes-2024-11-25-RNv0.77.0-d4f25d534ab744866448b36ca3bf3d97c08e638c
1701+
NitroModules:
1702+
:path: "../../../node_modules/react-native-nitro-modules"
16521703
RCT-Folly:
16531704
:podspec: "../../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
16541705
RCTDeprecation:
@@ -1769,6 +1820,7 @@ EXTERNAL SOURCES:
17691820
:path: "../../../node_modules/react-native/ReactCommon/yoga"
17701821

17711822
SPEC CHECKSUMS:
1823+
BluetoothStateManager: 598d565ceff2ad482220799c1c82b980a2ddae17
17721824
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
17731825
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
17741826
EXConstants: a1f35b9aabbb3c6791f8e67722579b1ffcdd3f18
@@ -1783,6 +1835,7 @@ SPEC CHECKSUMS:
17831835
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
17841836
glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8
17851837
hermes-engine: 1f783c3d53940aed0d2c84586f0b7a85ab7827ef
1838+
NitroModules: 584c0d7f68712f46353a8ab687d68bf1604f6c13
17861839
RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809
17871840
RCTDeprecation: f5c19ebdb8804b53ed029123eb69914356192fc8
17881841
RCTRequired: 6ae6cebe470486e0e0ce89c1c0eabb998e7c51f4

apps/expo-example-app/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99
"dependencies": {
1010
"expo": "~52.0.46",
1111
"react": "18.3.1",
12-
"react-native": "0.76.9"
12+
"react-native": "0.76.9",
13+
"react-native-nitro-modules": "*",
14+
"react-native-bluetooth-state-manager": "*"
1315
},
1416
"prettier": {
1517
"semi": false,
1618
"singleQuote": true,
1719
"trailingComma": "all"
18-
}
20+
},
21+
"devDependencies": {}
1922
}
23+

apps/expo-example-app/src/app/App.tsx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
import { SafeAreaView, StatusBar, Text } from 'react-native'
1+
import { useState } from 'react'
2+
import { Pressable, SafeAreaView, StatusBar, Text } from 'react-native'
3+
import { useBluetoothState } from 'react-native-bluetooth-state-manager'
24

35
export const App = () => {
6+
const [items, setItems] = useState<number[]>([...Array(5)].map((_, i) => i))
7+
console.log({ items })
8+
49
return (
510
<>
611
<StatusBar barStyle="dark-content" />
@@ -12,9 +17,43 @@ export const App = () => {
1217
}}
1318
>
1419
<Text>Example Expo App</Text>
20+
{items.map((_, index) => {
21+
return (
22+
<Pressable
23+
key={index}
24+
onPress={() => {
25+
setItems((prev) => {
26+
const next = [
27+
...prev.slice(0, index),
28+
...prev.slice(index + 1),
29+
]
30+
return next
31+
})
32+
}}
33+
>
34+
<Text>{index}.</Text>
35+
<Listener />
36+
</Pressable>
37+
)
38+
})}
39+
<Pressable
40+
onPress={() => {
41+
setItems((prev) => {
42+
return [...prev, prev.length]
43+
})
44+
}}
45+
>
46+
<Text>Add</Text>
47+
</Pressable>
1548
</SafeAreaView>
1649
</>
1750
)
1851
}
1952

53+
const Listener = () => {
54+
const bleState = useBluetoothState()
55+
console.log({ bleState })
56+
return <Text>{bleState}</Text>
57+
}
58+
2059
export default App

bun.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"react": "~18.3.1",
1313
"react-dom": "~18.3.1",
1414
"react-native": "0.76.3",
15+
"react-native-nitro-modules": "^0.25.2",
1516
"react-native-svg": "~15.8.0",
1617
"react-native-svg-transformer": "~1.5.0",
1718
"react-native-web": "~0.19.13",
@@ -71,6 +72,8 @@
7172
"expo": "~52.0.46",
7273
"react": "18.3.1",
7374
"react-native": "0.76.9",
75+
"react-native-bluetooth-state-manager": "*",
76+
"react-native-nitro-modules": "*",
7477
},
7578
},
7679
"packages/react-native-bluetooth-state-manager": {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"react": "~18.3.1",
4242
"react-dom": "~18.3.1",
4343
"react-native": "0.76.3",
44+
"react-native-nitro-modules": "^0.25.2",
4445
"react-native-svg": "~15.8.0",
4546
"react-native-svg-transformer": "~1.5.0",
4647
"react-native-web": "~0.19.13"

packages/react-native-bluetooth-state-manager/android/src/main/java/com/margelo/nitro/bluetoothstatemanager/BluetoothStateManager.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.facebook.react.bridge.BaseActivityEventListener
1313
import com.facebook.react.bridge.ReactApplicationContext
1414
import com.margelo.nitro.NitroModules
1515
import com.margelo.nitro.core.Promise
16+
import java.util.UUID
1617

1718
class BluetoothStateManager : HybridBluetoothStateManagerSpec() {
1819
override val memorySize: Long
@@ -36,7 +37,7 @@ class BluetoothStateManager : HybridBluetoothStateManagerSpec() {
3637
when (intent?.action) {
3738
BluetoothAdapter.ACTION_STATE_CHANGED -> {
3839
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
39-
for (listener in listeners) {
40+
for (listener in listeners.values) {
4041
listener(fromBluetoothState(state))
4142
}
4243
}
@@ -82,21 +83,22 @@ class BluetoothStateManager : HybridBluetoothStateManagerSpec() {
8283
}
8384
}
8485

85-
private var listeners = mutableListOf<(state: BluetoothState) -> Unit>()
86+
private val listeners = mutableMapOf<Any, (BluetoothState) -> Unit>()
8687

87-
override fun addListener(callback: (state: BluetoothState) -> Unit): Double {
88-
if (this.listeners.size == 0) {
88+
override fun addListener(callback: (state: BluetoothState) -> Unit): String {
89+
if (this.listeners.isEmpty()) {
8990
this.startListenForBluetoothStateChange()
9091
}
9192

92-
this.listeners.add(callback)
93-
return this.listeners.size.toDouble() - 1
93+
val key = UUID.randomUUID().toString()
94+
this.listeners[key] = callback
95+
return key
9496
}
9597

96-
override fun removeListener(index: Double) {
97-
this.listeners.removeAt(index.toInt())
98-
if (this.listeners.size == 0) {
99-
this.stopListenForBluetoothStateChange()
98+
override fun removeListener(callbackRef: String) {
99+
this.listeners.remove(callbackRef)
100+
if (this.listeners.isEmpty()) {
101+
stopListenForBluetoothStateChange()
100102
}
101103
}
102104

packages/react-native-bluetooth-state-manager/ios/HybridBluetoothStateManager.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,17 @@ class Listener {
5151
}
5252

5353
class HybridBluetoothStateManager: HybridBluetoothStateManagerSpec {
54-
var listeners: [Listener] = []
54+
var listeners: [String: Listener] = [:]
5555

56-
func addListener(callback: @escaping (BluetoothState) -> Void) throws -> Double {
56+
func addListener(callback: @escaping (BluetoothState) -> Void) throws -> String {
5757
let listener = Listener(callback: callback)
58-
listeners.append(listener)
59-
return Double(listeners.count) - 1.0
58+
let key = UUID().uuidString
59+
listeners[key] = listener
60+
return key
6061
}
6162

62-
func removeListener(index: Double) throws {
63-
listeners.remove(at: Int(index))
63+
func removeListener(callbackRef: String) throws {
64+
listeners.removeValue(forKey: callbackRef)
6465
}
6566

6667
var centralManager: CBCentralManager!
@@ -69,7 +70,7 @@ class HybridBluetoothStateManager: HybridBluetoothStateManagerSpec {
6970
override init() {
7071
super.init()
7172
self.bManager = CentralManager.init { state in
72-
for listener in self.listeners {
73+
for listener in self.listeners.values {
7374
listener.callback(state)
7475
}
7576
}

0 commit comments

Comments
 (0)