summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--App.tsx118
-rw-r--r--__tests__/App.test.tsx2
-rw-r--r--android/app/src/debug/AndroidManifest.xml42
-rw-r--r--android/app/src/main/AndroidManifest.xml53
-rwxr-xr-xdev-env10
-rw-r--r--index.js2
-rw-r--r--package-lock.json21
-rw-r--r--package.json4
-rw-r--r--src/App.tsx107
-rw-r--r--src/ble-service.ts186
10 files changed, 412 insertions, 133 deletions
diff --git a/App.tsx b/App.tsx
deleted file mode 100644
index 125fe1b..0000000
--- a/App.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
1/**
2 * Sample React Native App
3 * https://github.com/facebook/react-native
4 *
5 * @format
6 */
7
8import React from 'react';
9import type {PropsWithChildren} from 'react';
10import {
11 SafeAreaView,
12 ScrollView,
13 StatusBar,
14 StyleSheet,
15 Text,
16 useColorScheme,
17 View,
18} from 'react-native';
19
20import {
21 Colors,
22 DebugInstructions,
23 Header,
24 LearnMoreLinks,
25 ReloadInstructions,
26} from 'react-native/Libraries/NewAppScreen';
27
28type SectionProps = PropsWithChildren<{
29 title: string;
30}>;
31
32function Section({children, title}: SectionProps): React.JSX.Element {
33 const isDarkMode = useColorScheme() === 'dark';
34 return (
35 <View style={styles.sectionContainer}>
36 <Text
37 style={[
38 styles.sectionTitle,
39 {
40 color: isDarkMode ? Colors.white : Colors.black,
41 },
42 ]}>
43 {title}
44 </Text>
45 <Text
46 style={[
47 styles.sectionDescription,
48 {
49 color: isDarkMode ? Colors.light : Colors.dark,
50 },
51 ]}>
52 {children}
53 </Text>
54 </View>
55 );
56}
57
58function App(): React.JSX.Element {
59 const isDarkMode = useColorScheme() === 'dark';
60
61 const backgroundStyle = {
62 backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
63 };
64
65 return (
66 <SafeAreaView style={backgroundStyle}>
67 <StatusBar
68 barStyle={isDarkMode ? 'light-content' : 'dark-content'}
69 backgroundColor={backgroundStyle.backgroundColor}
70 />
71 <ScrollView
72 contentInsetAdjustmentBehavior="automatic"
73 style={backgroundStyle}>
74 <Header />
75 <View
76 style={{
77 backgroundColor: isDarkMode ? Colors.black : Colors.white,
78 }}>
79 <Section title="Step One">
80 Edit <Text style={styles.highlight}>App.tsx</Text> to change this
81 screen and then come back to see your edits.
82 </Section>
83 <Section title="See Your Changes">
84 <ReloadInstructions />
85 </Section>
86 <Section title="Debug">
87 <DebugInstructions />
88 </Section>
89 <Section title="Learn More">
90 Read the docs to discover what to do next:
91 </Section>
92 <LearnMoreLinks />
93 </View>
94 </ScrollView>
95 </SafeAreaView>
96 );
97}
98
99const styles = StyleSheet.create({
100 sectionContainer: {
101 marginTop: 32,
102 paddingHorizontal: 24,
103 },
104 sectionTitle: {
105 fontSize: 24,
106 fontWeight: '600',
107 },
108 sectionDescription: {
109 marginTop: 8,
110 fontSize: 18,
111 fontWeight: '400',
112 },
113 highlight: {
114 fontWeight: '700',
115 },
116});
117
118export default App;
diff --git a/__tests__/App.test.tsx b/__tests__/App.test.tsx
index 9eac6fb..d5d3244 100644
--- a/__tests__/App.test.tsx
+++ b/__tests__/App.test.tsx
@@ -4,7 +4,7 @@
4 4
5import 'react-native'; 5import 'react-native';
6import React from 'react'; 6import React from 'react';
7import App from '../App'; 7import App from '../src/App.tsx';
8 8
9// Note: import explicitly to use the types shipped with jest. 9// Note: import explicitly to use the types shipped with jest.
10import {it} from '@jest/globals'; 10import {it} from '@jest/globals';
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index eb98c01..b7c3a3c 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -1,6 +1,46 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<manifest xmlns:android="http://schemas.android.com/apk/res/android" 2<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"> 3 xmlns:tools="http://schemas.android.com/tools">
4
5 <uses-permission android:name="android.permission.INTERNET" />
6
7 <!--
8 HACK: this permission should not be needed on android 12+ devices anymore,
9 but in fact some manufacturers still need it for BLE to properly work :
10 https://stackoverflow.com/a/72370969
11 -->
12 <uses-permission android:name="android.permission.BLUETOOTH" tools:remove="android:maxSdkVersion" />
13 <!--
14 should normally only be needed on android < 12 if you want to:
15 - activate bluetooth programmatically
16 - discover local BLE devices
17 see: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#discover-local-devices.
18 Same as above, may still be wrongly needed by some manufacturers on android 12+.
19 -->
20 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:remove="android:maxSdkVersion" />
21
22 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
23 <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
24
25 <!-- Only when targeting Android 12 or higher -->
26 <!--
27 Please make sure you read the following documentation
28 to have a better understanding of the new permissions.
29 https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location
30 -->
31
32 <!-- Needed if your app search for Bluetooth devices. -->
33 <!--
34 If your app doesn't use Bluetooth scan results to derive physical location information,
35 you can strongly assert that your app doesn't derive physical location.
36 -->
37 <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
38 android:usesPermissionFlags="neverForLocation" />
39 <!-- Needed if you want to interact with a BLE device. -->
40 <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
41 <!-- Needed if your app makes the current device discoverable to other Bluetooth devices. -->
42 <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
43
4 44
5 <application 45 <application
6 android:usesCleartextTraffic="true" 46 android:usesCleartextTraffic="true"
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 4122f36..e1c8b57 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,25 +1,58 @@
1<manifest xmlns:android="http://schemas.android.com/apk/res/android"> 1<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools">
2 3
3 <uses-permission android:name="android.permission.INTERNET" /> 4 <uses-permission android:name="android.permission.INTERNET" />
4 5
5 <application 6 <uses-permission android:name="android.permission.BLUETOOTH" tools:remove="android:maxSdkVersion" />
7 <!--
8 should normally only be needed on android < 12 if you want to:
9 - activate bluetooth programmatically
10 - discover local BLE devices
11 see: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#discover-local-devices.
12 Same as above, may still be wrongly needed by some manufacturers on android 12+.
13 -->
14 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:remove="android:maxSdkVersion" />
15
16 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
17 <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
18
19 <!-- Only when targeting Android 12 or higher -->
20 <!--
21 Please make sure you read the following documentation
22 to have a better understanding of the new permissions.
23 https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location
24 -->
25
26 <!-- Needed if your app search for Bluetooth devices. -->
27 <!--
28 If your app doesn't use Bluetooth scan results to derive physical location information,
29 you can strongly assert that your app doesn't derive physical location.
30 -->
31 <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
32 android:usesPermissionFlags="neverForLocation" />
33 <!-- Needed if you want to interact with a BLE device. -->
34 <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
35 <!-- Needed if your app makes the current device discoverable to other Bluetooth devices. -->
36 <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
37
38 <application
6 android:name=".MainApplication" 39 android:name=".MainApplication"
7 android:label="@string/app_name" 40 android:label="@string/app_name"
8 android:icon="@mipmap/ic_launcher" 41 android:icon="@mipmap/ic_launcher"
9 android:roundIcon="@mipmap/ic_launcher_round" 42 android:roundIcon="@mipmap/ic_launcher_round"
10 android:allowBackup="false" 43 android:allowBackup="false"
11 android:theme="@style/AppTheme"> 44 android:theme="@style/AppTheme">
12 <activity 45 <activity
13 android:name=".MainActivity" 46 android:name=".MainActivity"
14 android:label="@string/app_name" 47 android:label="@string/app_name"
15 android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" 48 android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
16 android:launchMode="singleTask" 49 android:launchMode="singleTask"
17 android:windowSoftInputMode="adjustResize" 50 android:windowSoftInputMode="adjustResize"
18 android:exported="true"> 51 android:exported="true">
19 <intent-filter> 52 <intent-filter>
20 <action android:name="android.intent.action.MAIN" /> 53 <action android:name="android.intent.action.MAIN" />
21 <category android:name="android.intent.category.LAUNCHER" /> 54 <category android:name="android.intent.category.LAUNCHER" />
22 </intent-filter> 55 </intent-filter>
23 </activity> 56 </activity>
24 </application> 57 </application>
25</manifest> 58</manifest>
diff --git a/dev-env b/dev-env
new file mode 100755
index 0000000..452ee7a
--- /dev/null
+++ b/dev-env
@@ -0,0 +1,10 @@
1#!/bin/sh
2
3docker run -it \
4 --rm \
5 -v "$(pwd)":/app -w /app \
6 -v react-native-android-data:/root \
7 --privileged \
8 reactnativecommunity/react-native-android \
9 $@
10
diff --git a/index.js b/index.js
index a850d03..85f14e6 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,7 @@
3 */ 3 */
4 4
5import {AppRegistry} from 'react-native'; 5import {AppRegistry} from 'react-native';
6import App from './App'; 6import App from './src/App.tsx';
7import {name as appName} from './app.json'; 7import {name as appName} from './app.json';
8 8
9AppRegistry.registerComponent(appName, () => App); 9AppRegistry.registerComponent(appName, () => App);
diff --git a/package-lock.json b/package-lock.json
index 930ef30..f52af31 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,9 @@
9 "version": "0.0.1", 9 "version": "0.0.1",
10 "dependencies": { 10 "dependencies": {
11 "react": "18.2.0", 11 "react": "18.2.0",
12 "react-native": "0.73.0" 12 "react-native": "0.73.0",
13 "react-native-ble-manager": "^11.0.5",
14 "react-native-toast-message": "^2.1.8"
13 }, 15 },
14 "devDependencies": { 16 "devDependencies": {
15 "@babel/core": "^7.20.0", 17 "@babel/core": "^7.20.0",
@@ -11769,6 +11771,23 @@
11769 "react": "18.2.0" 11771 "react": "18.2.0"
11770 } 11772 }
11771 }, 11773 },
11774 "node_modules/react-native-ble-manager": {
11775 "version": "11.0.5",
11776 "resolved": "https://registry.npmjs.org/react-native-ble-manager/-/react-native-ble-manager-11.0.5.tgz",
11777 "integrity": "sha512-d8q6YEKyNkiygCy+qHa+N139UHRSWtbz7S+29mL0ZVjCSdzA6PRPsnSVDeRQJEVrWzP8ut2O6py57b6mPnBBFA==",
11778 "peerDependencies": {
11779 "react-native": ">=0.60.0"
11780 }
11781 },
11782 "node_modules/react-native-toast-message": {
11783 "version": "2.1.8",
11784 "resolved": "https://registry.npmjs.org/react-native-toast-message/-/react-native-toast-message-2.1.8.tgz",
11785 "integrity": "sha512-vlDQVkvpSq9L3/GRfoKaxVQyenj/A7yHToC9M0nrSCTR9XPXzoc2AOgRQnM6GVvDhetDld9ZfGtFcucyWcGQNA==",
11786 "peerDependencies": {
11787 "react": "*",
11788 "react-native": "*"
11789 }
11790 },
11772 "node_modules/react-native/node_modules/@jest/types": { 11791 "node_modules/react-native/node_modules/@jest/types": {
11773 "version": "26.6.2", 11792 "version": "26.6.2",
11774 "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", 11793 "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
diff --git a/package.json b/package.json
index c51391e..7455929 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,9 @@
11 }, 11 },
12 "dependencies": { 12 "dependencies": {
13 "react": "18.2.0", 13 "react": "18.2.0",
14 "react-native": "0.73.0" 14 "react-native": "0.73.0",
15 "react-native-ble-manager": "^11.0.5",
16 "react-native-toast-message": "^2.1.8"
15 }, 17 },
16 "devDependencies": { 18 "devDependencies": {
17 "@babel/core": "^7.20.0", 19 "@babel/core": "^7.20.0",
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000..7972da9
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,107 @@
1import React, {useEffect, useState} from 'react';
2import type {PropsWithChildren} from 'react';
3import {
4 Button,
5 FlatList,
6 SafeAreaView,
7 ScrollView,
8 StatusBar,
9 StyleSheet,
10 Text,
11 useColorScheme,
12 View,
13} from 'react-native';
14
15import Toast from 'react-native-toast-message';
16import BleService, {Peripheral} from './ble-service';
17
18const bleService = new BleService();
19
20function App(): React.JSX.Element {
21 bleService: BleService;
22
23 const [isScanning, setIsScanning] = useState<boolean>(false);
24 const [peripherals, setPeripherals] = useState<
25 Map<Peripheral['id'], Peripheral>
26 >([]);
27
28 useEffect(() => {
29 bleService.setEvents({
30 bleManagerConnectPeripheral: e => console.log(e.peripheral),
31 bleManagerStartSuccess: () => {
32 Toast.show({
33 type: 'info',
34 text1: 'BleManager started..',
35 });
36 },
37 bleManagerDiscoverPeripheral: p => {
38 console.log(p.advertising.localName);
39 },
40 bleManagerStopScan: () => {
41 setIsScanning(false);
42 setPeripherals(bleService.getPeripherals());
43
44 console.log('scan: stop;');
45 Toast.show({
46 type: 'success',
47 text1: 'Scan stop..',
48 position: 'bottom',
49 });
50 },
51 bleManagerStartScan: () => {
52 setIsScanning(true);
53 console.log('scan: start;');
54 Toast.show({
55 type: 'info',
56 text1: 'Scan start..',
57 position: 'bottom',
58 });
59 },
60 bleManagerDidUpdateValueForCharacteristic: a => {
61 console.log(a.value);
62 },
63 });
64
65 return () => bleService.destroy();
66 });
67
68 return (
69 <>
70 <Toast />
71 <View>
72 <Text>Hello, World!!!!</Text>
73 {isScanning ? <Text>SKANUJE!</Text> : <Text>Juz nie</Text>}
74 <Button
75 onPress={() => {
76 bleService.scan();
77 }}
78 title="Scan!"
79 />
80 <FlatList
81 data={[...peripherals.values()]}
82 renderItem={({item}) => (
83 <>
84 <Text>id: {item.id}</Text>
85 <Text>name: {item.name}</Text>
86 <Button
87 onPress={() => {
88 bleService.connect(item);
89 }}
90 title="Connect!"
91 />
92 <Button
93 onPress={() => {
94 console.log(bleService.read(item));
95 }}
96 title="Read!"
97 />
98 <Text>-------</Text>
99 </>
100 )}
101 />
102 </View>
103 </>
104 );
105}
106
107export default App;
diff --git a/src/ble-service.ts b/src/ble-service.ts
new file mode 100644
index 0000000..f2a08a3
--- /dev/null
+++ b/src/ble-service.ts
@@ -0,0 +1,186 @@
1import BleManager, {
2 BleConnectPeripheralEvent,
3 BleDisconnectPeripheralEvent,
4 BleManagerDidUpdateValueForCharacteristicEvent,
5 BleScanCallbackType,
6 BleScanMatchMode,
7 BleScanMode,
8 Peripheral as PeripheralWithoutConnectInfo,
9} from 'react-native-ble-manager';
10import {
11 EmitterSubscription,
12 NativeEventEmitter,
13 NativeModules,
14 PermissionsAndroid,
15 Platform,
16} from 'react-native';
17
18import Buffer from 'buffer';
19
20export type Peripheral = PeripheralWithoutConnectInfo & {
21 connected?: boolean;
22 connecting?: boolean;
23};
24
25type Events = {
26 bleManagerStartSuccess: () => void | undefined;
27 bleManagerStartError: () => void | undefined;
28 bleManagerStopScan: () => void | undefined;
29 bleManagerStartScan: () => void | undefined;
30 bleManagerDiscoverPeripheral: (peripheral: Peripheral) => void | undefined;
31 bleManagerDisconnectPeripheral: (
32 event: BleDisconnectPeripheralEvent,
33 ) => void | undefined;
34 bleManagerDidUpdateValueForCharacteristic: (
35 event: BleManagerDidUpdateValueForCharacteristicEvent,
36 ) => void | undefined;
37 bleManagerConnectPeripheral: (
38 event: BleConnectPeripheralEvent,
39 ) => void | undefined;
40};
41
42export default class BleService {
43 private _bleManagerModule = NativeModules.BleManager;
44 private _bleManagerEmitter = new NativeEventEmitter(this._bleManagerModule);
45 private _events: Events;
46 private _listeners: EmitterSubscription[];
47 private _peripherals: Map<Peripheral['id'], Peripheral>;
48
49 constructor() {
50 this._listeners = [];
51 this._events = {};
52 this._peripherals = new Map();
53 BleManager.start({showAlert: false})
54 .then(() => {
55 this.runEvent(this._events.bleManagerStartSuccess);
56 console.debug('[BleService]: BleManager started.');
57 })
58 .catch((err: any) => {
59 this.runEvent(this._events.bleManagerStartError);
60 console.debug('[BleService]: BeManager could not be started.', err);
61 });
62 this.handle_permissions();
63 }
64
65 setEvents(events: Events) {
66 this._events = events;
67 this.setupListiners(events);
68 }
69
70 handle_permissions() {
71 // po requestach, jak sie zrobi .then() nastepnie gdy (resoult) cos zwroci to oznacza, że ma perma jak nie to nie ma.
72 if (Platform.OS === 'android') {
73 if (Platform.Version >= 31)
74 PermissionsAndroid.requestMultiple([
75 PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
76 PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
77 ]);
78 else if (Platform.Version >= 23)
79 PermissionsAndroid.request(
80 PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
81 );
82 }
83 }
84
85 scan() {
86 this.runEvent(this._events.bleManagerStartScan);
87 this.clearPeripherals();
88 BleManager.scan([], 3, false, {
89 matchMode: BleScanMatchMode.Sticky,
90 scanMode: BleScanMode.LowLatency,
91 callbackType: BleScanCallbackType.AllMatches,
92 })
93 .then(() => {
94 console.debug('[startScan] scan promise returned successfully.');
95 })
96 .catch((err: any) => {
97 console.error('[startScan] ble scan returned in error', err);
98 });
99 }
100
101 destroy() {
102 this.destroyListiners();
103 }
104
105 getPeripherals() {
106 return this._peripherals;
107 }
108
109 connect(p: Peripheral) {
110 return BleManager.connect(p.id);
111 }
112
113 async read(peripheral: Peripheral) {
114 await BleManager.requestMTU(peripheral.id, 512);
115 BleManager.startNotification(
116 peripheral.id,
117 '6e400001-b5a3-f393-e0a9-e50e24dcca9e',
118 '6e400003-b5a3-f393-e0a9-e50e24dcca9e',
119 )
120 .then(a => {
121 // Success code
122 console.log('--------');
123 console.log(a);
124 console.log('--------');
125 })
126 .catch(error => {
127 // Failure code
128 console.log(error);
129 });
130 }
131
132 private addPeripheral(p: Peripheral) {
133 this._peripherals.set(p.id, p);
134 }
135
136 private clearPeripherals() {
137 this._peripherals = new Map();
138 }
139
140 private destroyListiners() {
141 for (const listener of this._listeners) {
142 if (listener) listener.remove();
143 }
144 }
145
146 private setupListiners(events: Events) {
147 this.destroyListiners();
148 this._listeners = [
149 this._bleManagerEmitter.addListener(
150 'BleManagerDiscoverPeripheral',
151 peripheral => {
152 this.addPeripheral(peripheral);
153 this.runEvent(events.bleManagerDiscoverPeripheral, peripheral);
154 },
155 ),
156 this._bleManagerEmitter.addListener('BleManagerStopScan', () =>
157 this.runEvent(events.bleManagerStopScan),
158 ),
159 this._bleManagerEmitter.addListener(
160 'BleManagerDisconnectPeripheral',
161 event => this.runEvent(events.bleManagerDisconnectPeripheral, event),
162 ),
163 this._bleManagerEmitter.addListener(
164 'BleManagerDidUpdateValueForCharacteristic',
165 event =>
166 this.runEvent(
167 events.bleManagerDidUpdateValueForCharacteristic,
168 event,
169 ),
170 ),
171 this._bleManagerEmitter.addListener(
172 'BleManagerConnectPeripheral',
173 event => this.runEvent(events.bleManagerConnectPeripheral, event),
174 ),
175 ];
176 }
177
178 private runEvent(
179 event: CallableFunction | undefined,
180 ...optionalParams: any[]
181 ): void {
182 if (event) {
183 event(...optionalParams);
184 }
185 }
186}