react-native-web-serial-api 0.0.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.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/android/build.gradle +62 -0
- package/android/gradle.properties +6 -0
- package/android/src/main/AndroidManifest.xml +21 -0
- package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +704 -0
- package/android/src/main/java/dev/webserialapi/NativeUsbSerialPackage.java +46 -0
- package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +235 -0
- package/android/src/main/java/dev/webserialapi/UsbDetachReceiver.java +37 -0
- package/android/src/main/res/xml/device_filter.xml +13 -0
- package/babel.config.js +3 -0
- package/biome.json +35 -0
- package/example/.watchmanconfig +1 -0
- package/example/App.tsx +71 -0
- package/example/__tests__/App.test.tsx +16 -0
- package/example/android/app/build.gradle +120 -0
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +10 -0
- package/example/android/app/src/debug/AndroidManifest.xml +9 -0
- package/example/android/app/src/main/AndroidManifest.xml +38 -0
- package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +22 -0
- package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +41 -0
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +3 -0
- package/example/android/app/src/main/res/values/styles.xml +9 -0
- package/example/android/build.gradle +22 -0
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/example/android/gradle.properties +47 -0
- package/example/android/gradlew +252 -0
- package/example/android/gradlew.bat +94 -0
- package/example/android/settings.gradle +6 -0
- package/example/app.json +4 -0
- package/example/babel.config.js +21 -0
- package/example/biome.json +47 -0
- package/example/deploy.sh +11 -0
- package/example/index.html +26 -0
- package/example/index.js +9 -0
- package/example/index.web.js +8 -0
- package/example/jest.config.js +12 -0
- package/example/metro.config.js +58 -0
- package/example/package-lock.json +14510 -0
- package/example/package.json +48 -0
- package/example/react-native.config.js +17 -0
- package/example/src/components/AppBar.tsx +73 -0
- package/example/src/components/Menu.tsx +90 -0
- package/example/src/components/SingleChoiceDialog.tsx +120 -0
- package/example/src/screens/ConnectScreen.tsx +195 -0
- package/example/src/screens/DevicesScreen.tsx +141 -0
- package/example/src/screens/TerminalScreen.tsx +564 -0
- package/example/src/settings.ts +43 -0
- package/example/src/theme.ts +19 -0
- package/example/src/util/TextUtil.ts +129 -0
- package/example/tsconfig.json +10 -0
- package/example/vite.config.mjs +55 -0
- package/lib/commonjs/NativeUsbSerial.js +11 -0
- package/lib/commonjs/NativeUsbSerial.js.map +1 -0
- package/lib/commonjs/NativeUsbSerial.web.js +12 -0
- package/lib/commonjs/NativeUsbSerial.web.js.map +1 -0
- package/lib/commonjs/UsbSerial.js +149 -0
- package/lib/commonjs/UsbSerial.js.map +1 -0
- package/lib/commonjs/WebSerial.js +852 -0
- package/lib/commonjs/WebSerial.js.map +1 -0
- package/lib/commonjs/index.js +44 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/serial.android.js +13 -0
- package/lib/commonjs/serial.android.js.map +1 -0
- package/lib/commonjs/serial.js +13 -0
- package/lib/commonjs/serial.js.map +1 -0
- package/lib/commonjs/serial.web.js +11 -0
- package/lib/commonjs/serial.web.js.map +1 -0
- package/lib/typescript/src/NativeUsbSerial.d.ts +51 -0
- package/lib/typescript/src/NativeUsbSerial.d.ts.map +1 -0
- package/lib/typescript/src/NativeUsbSerial.web.d.ts +3 -0
- package/lib/typescript/src/NativeUsbSerial.web.d.ts.map +1 -0
- package/lib/typescript/src/UsbSerial.d.ts +97 -0
- package/lib/typescript/src/UsbSerial.d.ts.map +1 -0
- package/lib/typescript/src/WebSerial.d.ts +236 -0
- package/lib/typescript/src/WebSerial.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +7 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/serial.android.d.ts +2 -0
- package/lib/typescript/src/serial.android.d.ts.map +1 -0
- package/lib/typescript/src/serial.d.ts +2 -0
- package/lib/typescript/src/serial.d.ts.map +1 -0
- package/lib/typescript/src/serial.web.d.ts +4 -0
- package/lib/typescript/src/serial.web.d.ts.map +1 -0
- package/package.json +78 -0
- package/react-native.config.js +9 -0
- package/scripts/deploy-release.sh +127 -0
- package/src/NativeUsbSerial.ts +124 -0
- package/src/NativeUsbSerial.web.ts +5 -0
- package/src/UsbSerial.ts +305 -0
- package/src/WebSerial.ts +1084 -0
- package/src/index.ts +23 -0
- package/src/lib/dom-exception.ts +161 -0
- package/src/lib/event-target.ts +170 -0
- package/src/lib/promise.ts +19 -0
- package/src/serial.android.ts +1 -0
- package/src/serial.ts +1 -0
- package/src/serial.web.ts +6 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
package dev.webserialapi;
|
|
2
|
+
|
|
3
|
+
import android.app.PendingIntent;
|
|
4
|
+
import android.content.BroadcastReceiver;
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.content.IntentFilter;
|
|
8
|
+
import android.hardware.usb.UsbDevice;
|
|
9
|
+
import android.hardware.usb.UsbDeviceConnection;
|
|
10
|
+
import android.hardware.usb.UsbManager;
|
|
11
|
+
|
|
12
|
+
import com.facebook.react.bridge.Arguments;
|
|
13
|
+
import com.facebook.react.bridge.Promise;
|
|
14
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
15
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
16
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
17
|
+
import com.facebook.react.bridge.WritableArray;
|
|
18
|
+
import com.facebook.react.bridge.WritableMap;
|
|
19
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
20
|
+
|
|
21
|
+
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
|
22
|
+
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
|
23
|
+
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
|
24
|
+
import com.hoho.android.usbserial.util.SerialInputOutputManager;
|
|
25
|
+
|
|
26
|
+
import android.app.Activity;
|
|
27
|
+
import android.content.ActivityNotFoundException;
|
|
28
|
+
|
|
29
|
+
import com.facebook.react.bridge.ActivityEventListener;
|
|
30
|
+
import com.facebook.react.bridge.BaseActivityEventListener;
|
|
31
|
+
|
|
32
|
+
import java.util.ArrayList;
|
|
33
|
+
import java.util.EnumSet;
|
|
34
|
+
import java.util.HashMap;
|
|
35
|
+
import java.util.List;
|
|
36
|
+
import java.util.Map;
|
|
37
|
+
|
|
38
|
+
public class NativeUsbSerialModule extends NativeUsbSerialSpec {
|
|
39
|
+
|
|
40
|
+
public static final String NAME = "NativeUsbSerial";
|
|
41
|
+
private static final String ACTION_USB_PERMISSION = "dev.webserialapi.USB_PERMISSION";
|
|
42
|
+
private static final String EXTRA_REQUEST_CODE = "dev.webserialapi.REQUEST_CODE";
|
|
43
|
+
|
|
44
|
+
private final UsbManager usbManager;
|
|
45
|
+
|
|
46
|
+
// key: "deviceId:portNumber"
|
|
47
|
+
private final Map<String, UsbSerialPort> openPorts = new HashMap<>();
|
|
48
|
+
private final Map<String, UsbDeviceConnection> openConnections = new HashMap<>();
|
|
49
|
+
private final Map<String, SerialInputOutputManager> ioManagers = new HashMap<>();
|
|
50
|
+
|
|
51
|
+
// key: requestCode
|
|
52
|
+
private final Map<Integer, Promise> pendingPermissions = new HashMap<>();
|
|
53
|
+
private int nextRequestCode = 0;
|
|
54
|
+
|
|
55
|
+
private static final int PORT_PICKER_REQUEST_CODE = 0xAB8465;
|
|
56
|
+
private Promise pendingPortPickerPromise = null;
|
|
57
|
+
|
|
58
|
+
private final ActivityEventListener activityEventListener = new BaseActivityEventListener() {
|
|
59
|
+
@Override
|
|
60
|
+
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
61
|
+
if (requestCode != PORT_PICKER_REQUEST_CODE) return;
|
|
62
|
+
|
|
63
|
+
Promise promise = pendingPortPickerPromise;
|
|
64
|
+
pendingPortPickerPromise = null;
|
|
65
|
+
|
|
66
|
+
if (promise == null) return;
|
|
67
|
+
|
|
68
|
+
if (resultCode != Activity.RESULT_OK || data == null) {
|
|
69
|
+
promise.reject("PORT_PICKER_CANCELED", "No port selected by the user.");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
int deviceId = data.getIntExtra(PortPickerActivity.EXTRA_DEVICE_ID, -1);
|
|
74
|
+
int portNumber = data.getIntExtra(PortPickerActivity.EXTRA_PORT_NUMBER, 0);
|
|
75
|
+
|
|
76
|
+
UsbSerialDriver driver = findDriver(deviceId);
|
|
77
|
+
if (driver == null) {
|
|
78
|
+
promise.reject("DRIVER_NOT_FOUND", "No driver found for deviceId: " + deviceId);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Request permission if not already granted, then resolve with PortId
|
|
83
|
+
requestPermission(deviceId, new PromiseWrapper(promise) {
|
|
84
|
+
@Override
|
|
85
|
+
public void onResolve(Object value) {
|
|
86
|
+
Boolean granted = (Boolean) value;
|
|
87
|
+
if (granted == null || !granted) {
|
|
88
|
+
promise.reject("PERMISSION_DENIED", "USB permission denied");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
WritableMap result = Arguments.createMap();
|
|
92
|
+
result.putInt("deviceId", deviceId);
|
|
93
|
+
result.putInt("portNumber", portNumber);
|
|
94
|
+
result.putInt("usbVendorId", driver.getDevice().getVendorId());
|
|
95
|
+
result.putInt("usbProductId", driver.getDevice().getProductId());
|
|
96
|
+
promise.resolve(result);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
private final BroadcastReceiver permissionReceiver = new BroadcastReceiver() {
|
|
103
|
+
@Override
|
|
104
|
+
public void onReceive(Context context, Intent intent) {
|
|
105
|
+
if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
|
|
106
|
+
int requestCode = intent.getIntExtra(EXTRA_REQUEST_CODE, -1);
|
|
107
|
+
boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
|
|
108
|
+
Promise promise = pendingPermissions.remove(requestCode);
|
|
109
|
+
if (promise != null) {
|
|
110
|
+
promise.resolve(granted);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
private final BroadcastReceiver usbStateReceiver = new BroadcastReceiver() {
|
|
117
|
+
@Override
|
|
118
|
+
public void onReceive(Context context, Intent intent) {
|
|
119
|
+
String action = intent.getAction();
|
|
120
|
+
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
|
121
|
+
if (device == null) return;
|
|
122
|
+
|
|
123
|
+
WritableMap event = Arguments.createMap();
|
|
124
|
+
event.putInt("deviceId", device.getDeviceId());
|
|
125
|
+
event.putInt("usbVendorId", device.getVendorId());
|
|
126
|
+
event.putInt("usbProductId", device.getProductId());
|
|
127
|
+
|
|
128
|
+
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
|
|
129
|
+
sendEvent("connect", event);
|
|
130
|
+
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
|
|
131
|
+
// Close any open ports for this device.
|
|
132
|
+
// The disconnect event is sent by UsbDetachReceiver.
|
|
133
|
+
String prefix = device.getDeviceId() + ":";
|
|
134
|
+
for (String key : new ArrayList<>(openPorts.keySet())) {
|
|
135
|
+
if (key.startsWith(prefix)) {
|
|
136
|
+
SerialInputOutputManager ioManager = ioManagers.remove(key);
|
|
137
|
+
if (ioManager != null) ioManager.stop();
|
|
138
|
+
UsbSerialPort port = openPorts.remove(key);
|
|
139
|
+
UsbDeviceConnection connection = openConnections.remove(key);
|
|
140
|
+
try { if (port != null) port.close(); } catch (Exception ignored) {}
|
|
141
|
+
try { if (connection != null) connection.close(); } catch (Exception ignored) {}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
public NativeUsbSerialModule(ReactApplicationContext reactContext) {
|
|
149
|
+
super(reactContext);
|
|
150
|
+
usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE);
|
|
151
|
+
|
|
152
|
+
IntentFilter permissionFilter = new IntentFilter(ACTION_USB_PERMISSION);
|
|
153
|
+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
|
154
|
+
reactContext.registerReceiver(permissionReceiver, permissionFilter, Context.RECEIVER_NOT_EXPORTED);
|
|
155
|
+
} else {
|
|
156
|
+
reactContext.registerReceiver(permissionReceiver, permissionFilter);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
reactContext.addActivityEventListener(activityEventListener);
|
|
160
|
+
|
|
161
|
+
IntentFilter usbFilter = new IntentFilter();
|
|
162
|
+
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
|
163
|
+
usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
|
164
|
+
reactContext.registerReceiver(usbStateReceiver, usbFilter);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Helper to avoid implementing all Promise methods in anonymous classes
|
|
168
|
+
private abstract static class PromiseWrapper implements Promise {
|
|
169
|
+
private final Promise delegate;
|
|
170
|
+
PromiseWrapper(Promise delegate) { this.delegate = delegate; }
|
|
171
|
+
public abstract void onResolve(Object value);
|
|
172
|
+
@Override public void resolve(Object value) { onResolve(value); }
|
|
173
|
+
@Override public void reject(String c) { delegate.reject(c); }
|
|
174
|
+
@Override public void reject(String c, String m) { delegate.reject(c, m); }
|
|
175
|
+
@Override public void reject(String c, Throwable e) { delegate.reject(c, e); }
|
|
176
|
+
@Override public void reject(String c, String m, Throwable e) { delegate.reject(c, m, e); }
|
|
177
|
+
@Override public void reject(Throwable e) { delegate.reject(e); }
|
|
178
|
+
@Override public void reject(Throwable e, WritableMap u) { delegate.reject(e, u); }
|
|
179
|
+
@Override public void reject(String c, WritableMap u) { delegate.reject(c, u); }
|
|
180
|
+
@Override public void reject(String c, String m, WritableMap u) { delegate.reject(c, m, u); }
|
|
181
|
+
@Override public void reject(String c, Throwable e, WritableMap u) { delegate.reject(c, e, u); }
|
|
182
|
+
@Override public void reject(String c, String m, Throwable e, WritableMap u) { delegate.reject(c, m, e, u); }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@Override
|
|
186
|
+
public String getName() {
|
|
187
|
+
return NAME;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// --- Helper ---
|
|
191
|
+
|
|
192
|
+
private String portKey(int deviceId, int portNumber) {
|
|
193
|
+
return deviceId + ":" + portNumber;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private UsbSerialPort getOpenPort(int deviceId, int portNumber, Promise promise) {
|
|
197
|
+
UsbSerialPort port = openPorts.get(portKey(deviceId, portNumber));
|
|
198
|
+
if (port == null) {
|
|
199
|
+
promise.reject("PORT_NOT_OPEN", "Port " + portKey(deviceId, portNumber) + " is not open");
|
|
200
|
+
}
|
|
201
|
+
return port;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private void sendEvent(String eventName, WritableMap params) {
|
|
205
|
+
getReactApplicationContext()
|
|
206
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
207
|
+
.emit(eventName, params);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private UsbSerialDriver findDriver(int deviceId) {
|
|
211
|
+
List<UsbSerialDriver> drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
|
|
212
|
+
for (UsbSerialDriver driver : drivers) {
|
|
213
|
+
if (driver.getDevice().getDeviceId() == deviceId) {
|
|
214
|
+
return driver;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// --- Spec implementation ---
|
|
221
|
+
|
|
222
|
+
@Override
|
|
223
|
+
public void findAllDrivers(Promise promise) {
|
|
224
|
+
try {
|
|
225
|
+
List<UsbSerialDriver> drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
|
|
226
|
+
WritableArray result = Arguments.createArray();
|
|
227
|
+
for (UsbSerialDriver driver : drivers) {
|
|
228
|
+
UsbDevice device = driver.getDevice();
|
|
229
|
+
List<UsbSerialPort> ports = driver.getPorts();
|
|
230
|
+
for (int i = 0; i < ports.size(); i++) {
|
|
231
|
+
WritableMap map = Arguments.createMap();
|
|
232
|
+
map.putInt("deviceId", device.getDeviceId());
|
|
233
|
+
map.putInt("portNumber", i);
|
|
234
|
+
map.putInt("usbVendorId", device.getVendorId());
|
|
235
|
+
map.putInt("usbProductId", device.getProductId());
|
|
236
|
+
result.pushMap(map);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
promise.resolve(result);
|
|
240
|
+
} catch (Exception e) {
|
|
241
|
+
promise.reject("FIND_DRIVERS_ERROR", e.getMessage(), e);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@Override
|
|
246
|
+
public void open(double deviceId, double portNumber, double baudRate, double dataBits, double stopBits, double parity, Promise promise) {
|
|
247
|
+
try {
|
|
248
|
+
int dId = (int) deviceId;
|
|
249
|
+
int pNum = (int) portNumber;
|
|
250
|
+
String key = portKey(dId, pNum);
|
|
251
|
+
|
|
252
|
+
if (openPorts.containsKey(key)) {
|
|
253
|
+
promise.reject("PORT_ALREADY_OPEN", "Port is already open");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
UsbSerialDriver driver = findDriver(dId);
|
|
258
|
+
if (driver == null) {
|
|
259
|
+
promise.reject("DRIVER_NOT_FOUND", "No driver found for deviceId: " + dId);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!usbManager.hasPermission(driver.getDevice())) {
|
|
264
|
+
// Permission not yet granted — request it and open the port when granted
|
|
265
|
+
final double fDeviceId = deviceId;
|
|
266
|
+
final double fPortNumber = portNumber;
|
|
267
|
+
final double fBaudRate = baudRate;
|
|
268
|
+
final double fDataBits = dataBits;
|
|
269
|
+
final double fStopBits = stopBits;
|
|
270
|
+
final double fParity = parity;
|
|
271
|
+
requestPermission(deviceId, new PromiseWrapper(promise) {
|
|
272
|
+
@Override
|
|
273
|
+
public void onResolve(Object value) {
|
|
274
|
+
Boolean granted = (Boolean) value;
|
|
275
|
+
if (granted != null && granted) {
|
|
276
|
+
open(fDeviceId, fPortNumber, fBaudRate, fDataBits, fStopBits, fParity, promise);
|
|
277
|
+
} else {
|
|
278
|
+
promise.reject("PERMISSION_DENIED", "USB permission denied");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
UsbDeviceConnection connection = usbManager.openDevice(driver.getDevice());
|
|
286
|
+
if (connection == null) {
|
|
287
|
+
promise.reject("CONNECTION_FAILED", "Could not open USB connection");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
UsbSerialPort port = driver.getPorts().get(pNum);
|
|
292
|
+
port.open(connection);
|
|
293
|
+
port.setParameters((int) baudRate, (int) dataBits, (int) stopBits, (int) parity);
|
|
294
|
+
|
|
295
|
+
openPorts.put(key, port);
|
|
296
|
+
openConnections.put(key, connection);
|
|
297
|
+
|
|
298
|
+
promise.resolve(null);
|
|
299
|
+
} catch (Exception e) {
|
|
300
|
+
promise.reject("OPEN_ERROR", e.getMessage(), e);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@Override
|
|
305
|
+
public void close(double deviceId, double portNumber, Promise promise) {
|
|
306
|
+
try {
|
|
307
|
+
int dId = (int) deviceId;
|
|
308
|
+
int pNum = (int) portNumber;
|
|
309
|
+
String key = portKey(dId, pNum);
|
|
310
|
+
|
|
311
|
+
SerialInputOutputManager ioManager = ioManagers.remove(key);
|
|
312
|
+
if (ioManager != null) {
|
|
313
|
+
ioManager.stop();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
UsbSerialPort port = openPorts.remove(key);
|
|
317
|
+
UsbDeviceConnection connection = openConnections.remove(key);
|
|
318
|
+
|
|
319
|
+
if (port != null) port.close();
|
|
320
|
+
if (connection != null) connection.close();
|
|
321
|
+
|
|
322
|
+
promise.resolve(null);
|
|
323
|
+
} catch (Exception e) {
|
|
324
|
+
promise.reject("CLOSE_ERROR", e.getMessage(), e);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@Override
|
|
329
|
+
public boolean isOpen(double deviceId, double portNumber) {
|
|
330
|
+
UsbSerialPort port = openPorts.get(portKey((int) deviceId, (int) portNumber));
|
|
331
|
+
return port != null && port.isOpen();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@Override
|
|
335
|
+
public void write(double deviceId, double portNumber, ReadableArray data, double timeout, Promise promise) {
|
|
336
|
+
try {
|
|
337
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
338
|
+
if (port == null) return;
|
|
339
|
+
|
|
340
|
+
byte[] bytes = new byte[data.size()];
|
|
341
|
+
for (int i = 0; i < data.size(); i++) {
|
|
342
|
+
bytes[i] = (byte) data.getInt(i);
|
|
343
|
+
}
|
|
344
|
+
port.write(bytes, (int) timeout);
|
|
345
|
+
promise.resolve(null);
|
|
346
|
+
} catch (Exception e) {
|
|
347
|
+
promise.reject("WRITE_ERROR", e.getMessage(), e);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@Override
|
|
352
|
+
public void startReading(double deviceId, double portNumber, Promise promise) {
|
|
353
|
+
try {
|
|
354
|
+
int dId = (int) deviceId;
|
|
355
|
+
int pNum = (int) portNumber;
|
|
356
|
+
String key = portKey(dId, pNum);
|
|
357
|
+
|
|
358
|
+
UsbSerialPort port = getOpenPort(dId, pNum, promise);
|
|
359
|
+
if (port == null) return;
|
|
360
|
+
|
|
361
|
+
if (ioManagers.containsKey(key)) {
|
|
362
|
+
promise.resolve(null);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
SerialInputOutputManager ioManager = new SerialInputOutputManager(port, new SerialInputOutputManager.Listener() {
|
|
367
|
+
@Override
|
|
368
|
+
public void onNewData(byte[] data) {
|
|
369
|
+
WritableArray bytes = Arguments.createArray();
|
|
370
|
+
for (byte b : data) {
|
|
371
|
+
bytes.pushInt(b & 0xFF);
|
|
372
|
+
}
|
|
373
|
+
WritableMap event = Arguments.createMap();
|
|
374
|
+
event.putInt("deviceId", dId);
|
|
375
|
+
event.putInt("portNumber", pNum);
|
|
376
|
+
event.putArray("data", bytes);
|
|
377
|
+
sendEvent("data", event);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@Override
|
|
381
|
+
public void onRunError(Exception e) {
|
|
382
|
+
ioManagers.remove(key);
|
|
383
|
+
// If the port was already removed by the detach handler, this is a
|
|
384
|
+
// disconnect-related error — don't fire an error event
|
|
385
|
+
if (!openPorts.containsKey(key)) return;
|
|
386
|
+
WritableMap event = Arguments.createMap();
|
|
387
|
+
event.putInt("deviceId", dId);
|
|
388
|
+
event.putInt("portNumber", pNum);
|
|
389
|
+
event.putString("error", e.getMessage());
|
|
390
|
+
sendEvent("error", event);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
ioManager.start();
|
|
395
|
+
ioManagers.put(key, ioManager);
|
|
396
|
+
promise.resolve(null);
|
|
397
|
+
} catch (Exception e) {
|
|
398
|
+
promise.reject("START_READING_ERROR", e.getMessage(), e);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
@Override
|
|
403
|
+
public void stopReading(double deviceId, double portNumber, Promise promise) {
|
|
404
|
+
String key = portKey((int) deviceId, (int) portNumber);
|
|
405
|
+
SerialInputOutputManager ioManager = ioManagers.remove(key);
|
|
406
|
+
if (ioManager != null) {
|
|
407
|
+
ioManager.stop();
|
|
408
|
+
}
|
|
409
|
+
promise.resolve(null);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@Override
|
|
413
|
+
public void setParameters(double deviceId, double portNumber, double baudRate, double dataBits, double stopBits, double parity, Promise promise) {
|
|
414
|
+
try {
|
|
415
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
416
|
+
if (port == null) return;
|
|
417
|
+
port.setParameters((int) baudRate, (int) dataBits, (int) stopBits, (int) parity);
|
|
418
|
+
promise.resolve(null);
|
|
419
|
+
} catch (Exception e) {
|
|
420
|
+
promise.reject("SET_PARAMETERS_ERROR", e.getMessage(), e);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
@Override
|
|
425
|
+
public void setDTR(double deviceId, double portNumber, boolean value, Promise promise) {
|
|
426
|
+
try {
|
|
427
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
428
|
+
if (port == null) return;
|
|
429
|
+
port.setDTR(value);
|
|
430
|
+
promise.resolve(null);
|
|
431
|
+
} catch (Exception e) {
|
|
432
|
+
promise.reject("SET_DTR_ERROR", e.getMessage(), e);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
@Override
|
|
437
|
+
public void setRTS(double deviceId, double portNumber, boolean value, Promise promise) {
|
|
438
|
+
try {
|
|
439
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
440
|
+
if (port == null) return;
|
|
441
|
+
port.setRTS(value);
|
|
442
|
+
promise.resolve(null);
|
|
443
|
+
} catch (Exception e) {
|
|
444
|
+
promise.reject("SET_RTS_ERROR", e.getMessage(), e);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@Override
|
|
449
|
+
public void getDTR(double deviceId, double portNumber, Promise promise) {
|
|
450
|
+
try {
|
|
451
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
452
|
+
if (port == null) return;
|
|
453
|
+
promise.resolve(port.getDTR());
|
|
454
|
+
} catch (Exception e) {
|
|
455
|
+
promise.reject("GET_DTR_ERROR", e.getMessage(), e);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@Override
|
|
460
|
+
public void getRTS(double deviceId, double portNumber, Promise promise) {
|
|
461
|
+
try {
|
|
462
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
463
|
+
if (port == null) return;
|
|
464
|
+
promise.resolve(port.getRTS());
|
|
465
|
+
} catch (Exception e) {
|
|
466
|
+
promise.reject("GET_RTS_ERROR", e.getMessage(), e);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
@Override
|
|
471
|
+
public void getCD(double deviceId, double portNumber, Promise promise) {
|
|
472
|
+
try {
|
|
473
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
474
|
+
if (port == null) return;
|
|
475
|
+
promise.resolve(port.getCD());
|
|
476
|
+
} catch (Exception e) {
|
|
477
|
+
promise.reject("GET_CD_ERROR", e.getMessage(), e);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
@Override
|
|
482
|
+
public void getCTS(double deviceId, double portNumber, Promise promise) {
|
|
483
|
+
try {
|
|
484
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
485
|
+
if (port == null) return;
|
|
486
|
+
promise.resolve(port.getCTS());
|
|
487
|
+
} catch (Exception e) {
|
|
488
|
+
promise.reject("GET_CTS_ERROR", e.getMessage(), e);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
@Override
|
|
493
|
+
public void getDSR(double deviceId, double portNumber, Promise promise) {
|
|
494
|
+
try {
|
|
495
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
496
|
+
if (port == null) return;
|
|
497
|
+
promise.resolve(port.getDSR());
|
|
498
|
+
} catch (Exception e) {
|
|
499
|
+
promise.reject("GET_DSR_ERROR", e.getMessage(), e);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
@Override
|
|
504
|
+
public void getRI(double deviceId, double portNumber, Promise promise) {
|
|
505
|
+
try {
|
|
506
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
507
|
+
if (port == null) return;
|
|
508
|
+
promise.resolve(port.getRI());
|
|
509
|
+
} catch (Exception e) {
|
|
510
|
+
promise.reject("GET_RI_ERROR", e.getMessage(), e);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
@Override
|
|
515
|
+
public void getControlLines(double deviceId, double portNumber, Promise promise) {
|
|
516
|
+
try {
|
|
517
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
518
|
+
if (port == null) return;
|
|
519
|
+
EnumSet<UsbSerialPort.ControlLine> lines = port.getControlLines();
|
|
520
|
+
WritableArray result = Arguments.createArray();
|
|
521
|
+
for (UsbSerialPort.ControlLine line : lines) {
|
|
522
|
+
result.pushString(line.name());
|
|
523
|
+
}
|
|
524
|
+
promise.resolve(result);
|
|
525
|
+
} catch (Exception e) {
|
|
526
|
+
promise.reject("GET_CONTROL_LINES_ERROR", e.getMessage(), e);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
@Override
|
|
531
|
+
public void getSupportedControlLines(double deviceId, double portNumber, Promise promise) {
|
|
532
|
+
try {
|
|
533
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
534
|
+
if (port == null) return;
|
|
535
|
+
EnumSet<UsbSerialPort.ControlLine> lines = port.getSupportedControlLines();
|
|
536
|
+
WritableArray result = Arguments.createArray();
|
|
537
|
+
for (UsbSerialPort.ControlLine line : lines) {
|
|
538
|
+
result.pushString(line.name());
|
|
539
|
+
}
|
|
540
|
+
promise.resolve(result);
|
|
541
|
+
} catch (Exception e) {
|
|
542
|
+
promise.reject("GET_SUPPORTED_CONTROL_LINES_ERROR", e.getMessage(), e);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
@Override
|
|
547
|
+
public void setFlowControl(double deviceId, double portNumber, String flowControl, Promise promise) {
|
|
548
|
+
try {
|
|
549
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
550
|
+
if (port == null) return;
|
|
551
|
+
port.setFlowControl(UsbSerialPort.FlowControl.valueOf(flowControl));
|
|
552
|
+
promise.resolve(null);
|
|
553
|
+
} catch (Exception e) {
|
|
554
|
+
promise.reject("SET_FLOW_CONTROL_ERROR", e.getMessage(), e);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
@Override
|
|
559
|
+
public void getFlowControl(double deviceId, double portNumber, Promise promise) {
|
|
560
|
+
try {
|
|
561
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
562
|
+
if (port == null) return;
|
|
563
|
+
promise.resolve(port.getFlowControl().name());
|
|
564
|
+
} catch (Exception e) {
|
|
565
|
+
promise.reject("GET_FLOW_CONTROL_ERROR", e.getMessage(), e);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
@Override
|
|
570
|
+
public void getSupportedFlowControl(double deviceId, double portNumber, Promise promise) {
|
|
571
|
+
try {
|
|
572
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
573
|
+
if (port == null) return;
|
|
574
|
+
EnumSet<UsbSerialPort.FlowControl> modes = port.getSupportedFlowControl();
|
|
575
|
+
WritableArray result = Arguments.createArray();
|
|
576
|
+
for (UsbSerialPort.FlowControl mode : modes) {
|
|
577
|
+
result.pushString(mode.name());
|
|
578
|
+
}
|
|
579
|
+
promise.resolve(result);
|
|
580
|
+
} catch (Exception e) {
|
|
581
|
+
promise.reject("GET_SUPPORTED_FLOW_CONTROL_ERROR", e.getMessage(), e);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
@Override
|
|
586
|
+
public void setBreak(double deviceId, double portNumber, boolean value, Promise promise) {
|
|
587
|
+
try {
|
|
588
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
589
|
+
if (port == null) return;
|
|
590
|
+
port.setBreak(value);
|
|
591
|
+
promise.resolve(null);
|
|
592
|
+
} catch (Exception e) {
|
|
593
|
+
promise.reject("SET_BREAK_ERROR", e.getMessage(), e);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
@Override
|
|
598
|
+
public void purgeHwBuffers(double deviceId, double portNumber, boolean purgeWriteBuffers, boolean purgeReadBuffers, Promise promise) {
|
|
599
|
+
try {
|
|
600
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
601
|
+
if (port == null) return;
|
|
602
|
+
port.purgeHwBuffers(purgeWriteBuffers, purgeReadBuffers);
|
|
603
|
+
promise.resolve(null);
|
|
604
|
+
} catch (Exception e) {
|
|
605
|
+
promise.reject("PURGE_HW_BUFFERS_ERROR", e.getMessage(), e);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
@Override
|
|
610
|
+
public void getSerial(double deviceId, double portNumber, Promise promise) {
|
|
611
|
+
try {
|
|
612
|
+
UsbSerialPort port = getOpenPort((int) deviceId, (int) portNumber, promise);
|
|
613
|
+
if (port == null) return;
|
|
614
|
+
promise.resolve(port.getSerial());
|
|
615
|
+
} catch (Exception e) {
|
|
616
|
+
promise.reject("GET_SERIAL_ERROR", e.getMessage(), e);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
@Override
|
|
621
|
+
public void requestPermission(double deviceId, Promise promise) {
|
|
622
|
+
try {
|
|
623
|
+
UsbSerialDriver driver = findDriver((int) deviceId);
|
|
624
|
+
if (driver == null) {
|
|
625
|
+
promise.reject("DRIVER_NOT_FOUND", "No driver found for deviceId: " + (int) deviceId);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (usbManager.hasPermission(driver.getDevice())) {
|
|
629
|
+
promise.resolve(true);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
int requestCode = nextRequestCode++;
|
|
634
|
+
pendingPermissions.put(requestCode, promise);
|
|
635
|
+
|
|
636
|
+
Intent intent = new Intent(ACTION_USB_PERMISSION);
|
|
637
|
+
intent.putExtra(EXTRA_REQUEST_CODE, requestCode);
|
|
638
|
+
|
|
639
|
+
PendingIntent permissionIntent = PendingIntent.getBroadcast(
|
|
640
|
+
getReactApplicationContext(),
|
|
641
|
+
requestCode,
|
|
642
|
+
intent,
|
|
643
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
644
|
+
);
|
|
645
|
+
usbManager.requestPermission(driver.getDevice(), permissionIntent);
|
|
646
|
+
} catch (Exception e) {
|
|
647
|
+
promise.reject("REQUEST_PERMISSION_ERROR", e.getMessage(), e);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
@Override
|
|
652
|
+
public void showPortPicker(ReadableArray filters, ReadableMap labels, Promise promise) {
|
|
653
|
+
Activity activity = getCurrentActivity();
|
|
654
|
+
if (activity == null) {
|
|
655
|
+
promise.reject("NO_ACTIVITY", "No current activity available");
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (pendingPortPickerPromise != null) {
|
|
659
|
+
promise.reject("PICKER_ALREADY_OPEN", "Port picker is already open");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
pendingPortPickerPromise = promise;
|
|
663
|
+
Intent intent = new Intent(getReactApplicationContext(), PortPickerActivity.class);
|
|
664
|
+
|
|
665
|
+
// Pass filters as parallel int arrays
|
|
666
|
+
if (filters != null && filters.size() > 0) {
|
|
667
|
+
int[] vendorIds = new int[filters.size()];
|
|
668
|
+
int[] productIds = new int[filters.size()];
|
|
669
|
+
for (int i = 0; i < filters.size(); i++) {
|
|
670
|
+
ReadableMap filter = filters.getMap(i);
|
|
671
|
+
vendorIds[i] = filter != null && filter.hasKey("usbVendorId") ? filter.getInt("usbVendorId") : -1;
|
|
672
|
+
productIds[i] = filter != null && filter.hasKey("usbProductId") ? filter.getInt("usbProductId") : -1;
|
|
673
|
+
}
|
|
674
|
+
intent.putExtra(PortPickerActivity.EXTRA_FILTERS_VENDOR_IDS, vendorIds);
|
|
675
|
+
intent.putExtra(PortPickerActivity.EXTRA_FILTERS_PRODUCT_IDS, productIds);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Pass optional label overrides to PortPickerActivity
|
|
679
|
+
if (labels != null) {
|
|
680
|
+
if (labels.hasKey("titleSelectPort"))
|
|
681
|
+
intent.putExtra(PortPickerActivity.EXTRA_TITLE_SELECT_PORT,
|
|
682
|
+
labels.getString("titleSelectPort"));
|
|
683
|
+
if (labels.hasKey("titleNoPortsAvailable"))
|
|
684
|
+
intent.putExtra(PortPickerActivity.EXTRA_TITLE_NO_PORTS_AVAILABLE,
|
|
685
|
+
labels.getString("titleNoPortsAvailable"));
|
|
686
|
+
if (labels.hasKey("messageNoPortsAvailable"))
|
|
687
|
+
intent.putExtra(PortPickerActivity.EXTRA_MESSAGE_NO_PORTS_AVAILABLE,
|
|
688
|
+
labels.getString("messageNoPortsAvailable"));
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
try {
|
|
692
|
+
activity.startActivityForResult(intent, PORT_PICKER_REQUEST_CODE);
|
|
693
|
+
} catch (ActivityNotFoundException e) {
|
|
694
|
+
pendingPortPickerPromise = null;
|
|
695
|
+
promise.reject("ACTIVITY_NOT_FOUND", e.getMessage(), e);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
@Override
|
|
700
|
+
public void addListener(String eventName) {}
|
|
701
|
+
|
|
702
|
+
@Override
|
|
703
|
+
public void removeListeners(double count) {}
|
|
704
|
+
}
|