@iotize/device-com-ble.cordova 3.2.1 → 3.3.2

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.
Files changed (49) hide show
  1. package/README.md +174 -177
  2. package/bundles/iotize-device-com-ble.cordova.umd.js +102 -69
  3. package/bundles/iotize-device-com-ble.cordova.umd.js.map +1 -1
  4. package/bundles/iotize-device-com-ble.cordova.umd.min.js +16 -0
  5. package/bundles/iotize-device-com-ble.cordova.umd.min.js.map +1 -0
  6. package/esm2015/lib/ble-com-protocol.js +10 -10
  7. package/esm2015/lib/ble-com-protocol.js.map +1 -1
  8. package/esm2015/lib/cordova-ble-error.js +11 -2
  9. package/esm2015/lib/cordova-ble-error.js.map +1 -1
  10. package/esm2015/lib/cordova-interface.js.map +1 -1
  11. package/esm2015/lib/cordova-service-adapter.js +15 -14
  12. package/esm2015/lib/cordova-service-adapter.js.map +1 -1
  13. package/esm2015/lib/cordova-service-adapter.metadata.json +1 -1
  14. package/esm2015/lib/definitions.js +0 -2
  15. package/esm2015/lib/definitions.js.map +1 -1
  16. package/esm2015/lib/iotize-ble-cordova-plugin.js +59 -51
  17. package/esm2015/lib/iotize-ble-cordova-plugin.js.map +1 -1
  18. package/esm2015/lib/iotize-ble-cordova-plugin.metadata.json +1 -1
  19. package/esm2015/lib/iotize-ble-cordova-plugin.ngsummary.json +1 -1
  20. package/esm2015/lib/logger.js +1 -1
  21. package/esm2015/lib/logger.js.map +1 -1
  22. package/esm2015/lib/scanner.js +14 -12
  23. package/esm2015/lib/scanner.js.map +1 -1
  24. package/esm2015/lib/scanner.metadata.json +1 -1
  25. package/esm2015/lib/utility.js +1 -1
  26. package/esm2015/lib/utility.js.map +1 -1
  27. package/esm2015/public_api.js +4 -4
  28. package/esm2015/public_api.js.map +1 -1
  29. package/fesm2015/iotize-device-com-ble.cordova.js +93 -74
  30. package/fesm2015/iotize-device-com-ble.cordova.js.map +1 -1
  31. package/iotize-device-com-ble.cordova.metadata.json +1 -1
  32. package/lib/ble-com-protocol.d.ts +4 -4
  33. package/lib/cordova-ble-error.d.ts +12 -3
  34. package/lib/cordova-service-adapter.d.ts +1 -1
  35. package/lib/definitions.d.ts +1 -1
  36. package/lib/iotize-ble-cordova-plugin.d.ts +4 -3
  37. package/lib/scanner.d.ts +4 -4
  38. package/package.json +1 -1
  39. package/plugin.xml +66 -63
  40. package/public_api.d.ts +5 -5
  41. package/src/android/.gradle/4.8.1/fileHashes/fileHashes.bin +0 -0
  42. package/src/android/.gradle/4.8.1/fileHashes/fileHashes.lock +0 -0
  43. package/src/android/build.gradle +1 -0
  44. package/src/android/src/ble/BLECom.java +559 -558
  45. package/src/ios/BLECom.swift +732 -732
  46. package/src/windows/IoTizeBLE.pdb +0 -0
  47. package/src/windows/IoTizeBLE.pri +0 -0
  48. package/src/windows/iotize-ble-com.js +159 -179
  49. package/www/plugin.js +1 -1
@@ -1,559 +1,560 @@
1
- //
2
- // Copyright 2018 IoTize SAS Inc. Licensed under the MIT license.
3
- //
4
- // BLECom.java
5
- // device-com-ble.cordova BLE Cordova Plugin
6
- //
7
-
8
- package com.iotize.plugin.cordova.ble;
9
-
10
- import static android.Manifest.permission.ACCESS_FINE_LOCATION;
11
-
12
- import android.annotation.SuppressLint;
13
- import android.app.Activity;
14
- import android.bluetooth.BluetoothAdapter;
15
- import android.bluetooth.BluetoothDevice;
16
- import android.bluetooth.BluetoothGatt;
17
- import android.bluetooth.BluetoothGattCharacteristic;
18
- import android.bluetooth.BluetoothGattService;
19
- import android.bluetooth.BluetoothManager;
20
- import android.content.Context;
21
- import android.content.Intent;
22
- import android.content.pm.PackageManager;
23
- import android.util.Log;
24
-
25
- import com.iotize.android.communication.protocol.ble.BLEProtocol;
26
- import com.iotize.android.communication.protocol.ble.DeviceManager;
27
- import com.iotize.android.communication.protocol.ble.characteristics.CharacteristicAdapter;
28
- import com.iotize.android.communication.protocol.ble.exception.CannotWriteCharacteristicException;
29
- import com.iotize.android.communication.protocol.ble.exception.CharacteristicNotAvailableException;
30
- import com.iotize.android.communication.protocol.ble.exception.ServiceNotAvailableException;
31
- import com.iotize.android.communication.protocol.ble.exception.WritePacketIsTooBigException;
32
- import com.iotize.android.communication.protocol.ble.scanner.BLEScanner;
33
- import com.iotize.android.core.util.Helper;
34
- import com.iotize.android.device.api.device.scanner.IOnDeviceDiscovered;
35
- import com.iotize.plugin.cordova.ble.models.RequestDeviceOptions;
36
-
37
- import org.apache.cordova.CallbackContext;
38
- import org.apache.cordova.CordovaInterface;
39
- import org.apache.cordova.CordovaPlugin;
40
- import org.apache.cordova.CordovaWebView;
41
- import org.apache.cordova.LOG;
42
- import org.apache.cordova.PermissionHelper;
43
- import org.jetbrains.annotations.NotNull;
44
- import org.json.JSONArray;
45
-
46
- import java.util.List;
47
- import java.util.UUID;
48
-
49
- import io.reactivex.annotations.NonNull;
50
- import io.reactivex.annotations.Nullable;
51
-
52
- public class BLECom extends CordovaPlugin {
53
- // actions
54
- private static final String CONNECT = "connect";
55
- private static final String CONNECT_AND_DISCOVER_TAP_SERVICES = "connectAndDiscoverTapServices";
56
- private static final String DISCONNECT = "disConnect";
57
- private static final String CLOSE = "close";
58
- private static final String SEND_REQUEST = "sendRequest";
59
- private static final String GET_LAST_ERROR = "getLastError";
60
- private static final String START_SCAN = "startScan";
61
- private static final String STOP_SCAN = "stopScan";
62
- private static final String IS_ENABLED = "isEnabled";
63
- private static final String CHECK_AVAILABLE = "checkAvailable";
64
- private static final String IS_CONNECTED = "isConnected";
65
- private static final String ENABLE = "enable";
66
- private static final String REQUEST_MTU = "requestMtu";
67
-
68
- // New
69
- private static final String CHARACTERISTIC_READ = "characteristicRead";
70
- private static final String CHARACTERISTIC_WRITE = "characteristicWrite";
71
- private static final String CHARACTERISTIC_WRITE_WITHOUT_RESPONSE = "characteristicWriteWithoutResponse";
72
- private static final String START_NOTIFICATION = "characteristicStartNotification";
73
- private static final String STOP_NOTIFICATION = "characteristicStopNotification";
74
- private static final String CHARACTERISTIC_CHANGED = "characteristicChanged";
75
- private static final String DISCOVER_SERVICES = "discoverServices";
76
-
77
-
78
- private static final int REQUEST_ENABLE_BLUETOOTH = 1;
79
- private static final int REQUEST_SCAN_PERMISSIONS = 2;
80
-
81
- private static final String TAG = "BLECom";
82
- private DeviceManager<BLEProtocol> peripherals;
83
- private BluetoothAdapter bluetoothAdapter;
84
- private PluginResponse enableBluetoothCallback;
85
- private PluginResponse pluginResponseDiscoverDevice;
86
- private PluginResponse permissionCallback;
87
- private BLEScanner scanner;
88
- private IOnDeviceDiscovered<BLEScanner.BLEScanData> onDeviceDiscoveredCallback;
89
- @Nullable
90
- private RequestDeviceOptions requestDeviceOptions;
91
-
92
- public void initialize(CordovaInterface cordova, CordovaWebView webView) {
93
- super.initialize(cordova, webView);
94
-
95
- this.peripherals = new DeviceManager<>();
96
- Log.d(TAG, "Initializing IoTizeBLE Plugin");
97
- }
98
-
99
- @Override
100
- public void onStart() {
101
- super.onStart();
102
- }
103
-
104
- @Override
105
- public void onStop() {
106
- super.onStop();
107
- }
108
-
109
- @SuppressLint("MissingPermission")
110
- public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) {
111
-
112
- PluginResponse pluginResponse = new PluginResponse(
113
- action,
114
- args,
115
- callbackContext
116
- );
117
- ArgsHelper argsHelper = new ArgsHelper(args);
118
- try {
119
- this.setupBluetoothAdapter();
120
- if (action.equals(STOP_SCAN)) {
121
- Log.d(TAG, STOP_SCAN);
122
- this.stopScan(pluginResponse);
123
- } else if (action.equals(START_SCAN)) {
124
- Log.d(TAG, START_SCAN);
125
- RequestDeviceOptions scanOptions = null;
126
- if (argsHelper.length() > 0) {
127
- scanOptions = argsHelper.parseJSONAt(0, RequestDeviceOptions.class);
128
- }
129
- this.startScan(scanOptions, pluginResponse);
130
- } else if (action.equals(CONNECT)) {
131
- Log.d(TAG, CONNECT);
132
- String macAddress = argsHelper.getString(0);
133
- this.connect(pluginResponse, macAddress);
134
- } else if (action.equals(REQUEST_MTU)) {
135
- Log.d(TAG, REQUEST_MTU);
136
- String macAddress = argsHelper.getString(0);
137
- int mtu = argsHelper.getInt(1);
138
- this.requestMtu(pluginResponse, macAddress, mtu);
139
- } else if (action.equals(CONNECT_AND_DISCOVER_TAP_SERVICES)) {
140
- Log.d(TAG, CONNECT_AND_DISCOVER_TAP_SERVICES);
141
- String macAddress = argsHelper.getString(0);
142
- this.connectAndDiscoverTapServices(pluginResponse, macAddress);
143
- } else if (action.equals(DISCONNECT)) {
144
- Log.d(TAG, DISCONNECT);
145
- String macAddress = argsHelper.getString(0);
146
- this.disconnect(pluginResponse, macAddress);
147
- } else if (action.equals(CLOSE)) {
148
- Log.d(TAG, CLOSE);
149
- String macAddress = argsHelper.getString(0);
150
- this.close(pluginResponse, macAddress);
151
- } else if (action.equals(GET_LAST_ERROR)) {
152
- Log.d(TAG, GET_LAST_ERROR);
153
- this.getLastError(pluginResponse);
154
- } else if (action.equals(SEND_REQUEST)) {
155
- String deviceId = argsHelper.getString(0);
156
- byte[] data = argsHelper.getBytes(1);
157
- this.sendLwm2mTapRequest(pluginResponse, deviceId, data);
158
- } else if (action.equals(CHECK_AVAILABLE) || action.equals(IS_ENABLED)) {
159
- pluginResponse.success(bluetoothAdapter.isEnabled());
160
- } else if (action.equals(IS_CONNECTED)) {
161
- String macAddress = argsHelper.getString(0);
162
- boolean isConnected = peripherals.get(macAddress).isConnected();
163
- pluginResponse.success(isConnected);
164
- } else if (action.equals(ENABLE)) {
165
- if (enableBluetoothCallback != null) {
166
- Log.w(TAG, "There is already an enable request pending...");
167
- }
168
- enableBluetoothCallback = pluginResponse;
169
- Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
170
- cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH);
171
- } else if (action.equals(CHARACTERISTIC_READ)) {
172
- String macAddress = argsHelper.getString(0);
173
- UUID serviceUUID = argsHelper.getUUID(1);
174
- UUID characteristicUUID = argsHelper.getUUID(2);
175
- this.readCharacteristicValue(pluginResponse, macAddress, serviceUUID, characteristicUUID);
176
- } else if (action.equals(CHARACTERISTIC_WRITE)) {
177
- String macAddress = argsHelper.getString(0);
178
- UUID serviceUUID = argsHelper.getUUID(1);
179
- UUID characteristicUUID = argsHelper.getUUID(2);
180
- byte[] data = argsHelper.getBytes(3);
181
- this.writeCharacteristic(pluginResponse, macAddress, serviceUUID, characteristicUUID, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
182
- } else if (action.equals(CHARACTERISTIC_WRITE_WITHOUT_RESPONSE)) {
183
- String macAddress = argsHelper.getString(0);
184
- UUID serviceUUID = argsHelper.getUUID(1);
185
- UUID characteristicUUID = argsHelper.getUUID(2);
186
- byte[] data = argsHelper.getBytes(3);
187
- this.writeCharacteristic(pluginResponse, macAddress, serviceUUID, characteristicUUID, data, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
188
- } else if (action.equals(START_NOTIFICATION)) {
189
- String macAddress = argsHelper.getString(0);
190
- UUID serviceUUID = argsHelper.getUUID(1);
191
- UUID characteristicUUID = argsHelper.getUUID(2);
192
- this.startNotification(pluginResponse, macAddress, serviceUUID, characteristicUUID);
193
- } else if (action.equals(STOP_NOTIFICATION)) {
194
- String macAddress = argsHelper.getString(0);
195
- UUID serviceUUID = argsHelper.getUUID(1);
196
- UUID characteristicUUID = argsHelper.getUUID(2);
197
- this.stopNotification(pluginResponse, macAddress, serviceUUID, characteristicUUID);
198
- } else if (action.equals(CHARACTERISTIC_CHANGED)) {
199
- String macAddress = argsHelper.getString(0);
200
- UUID serviceUUID = argsHelper.getUUID(1);
201
- UUID characteristicUUID = argsHelper.getUUID(2);
202
- this.listenToCharacteristicChange(pluginResponse, macAddress, serviceUUID, characteristicUUID);
203
- } else if (action.equals(DISCOVER_SERVICES)) {
204
- String macAddress = argsHelper.getString(0);
205
- this.discoverServices(pluginResponse, macAddress);
206
- } else {
207
- throw BLEComError.illegalAction("Illegal action " + action);
208
- }
209
- return true;
210
- } catch (BLEComError err) {
211
- pluginResponse.error(err);
212
- return false;
213
- } catch (Throwable err) {
214
- pluginResponse.error(BLEComError.internalError(err));
215
- return false;
216
- }
217
-
218
- }
219
-
220
- private void discoverServices(PluginResponse pluginResponse, String macAddress) throws Exception {
221
- // TODO async ?
222
- BLEProtocol peripheral = peripherals.get(macAddress);
223
- peripheral.discoverServices();
224
- List<BluetoothGattService> services = peripheral.getGatt().getServices();
225
- pluginResponse.success(JSONBuilder.toServicesDescription(services));
226
- }
227
-
228
- private void listenToCharacteristicChange(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws CharacteristicNotAvailableException, ServiceNotAvailableException {
229
- CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
230
- characteristic.onValueChanged(new CharacteristicAdapter.OnCharacteristicValueChangedCallback() {
231
- @Override
232
- public void onCharacteristicValueChanged(byte[] bytes) {
233
- pluginResponse.newResult(bytes);
234
- }
235
- });
236
- }
237
-
238
- private void setupBluetoothAdapter() throws BLEComError {
239
- if (bluetoothAdapter == null) {
240
- Activity activity = cordova.getActivity();
241
- boolean hardwareSupportsBLE = activity.getApplicationContext()
242
- .getPackageManager()
243
- .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
244
- if (!hardwareSupportsBLE) {
245
- LOG.w(TAG, "This hardware does not support Bluetooth Low Energy.");
246
- throw BLEComError.bleNotAvailable();
247
- }
248
- BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
249
- if (bluetoothManager == null) {
250
- throw BLEComError.bleNotAvailable();
251
- }
252
- bluetoothAdapter = bluetoothManager.getAdapter();
253
- }
254
-
255
- }
256
-
257
- private void startScan(@Nullable RequestDeviceOptions options, @NonNull PluginResponse pluginResponse) {
258
- if (pluginResponseDiscoverDevice != null && pluginResponseDiscoverDevice != pluginResponse) {
259
- Log.w(TAG, "Scan is already started...");
260
- }
261
- this.startBLEScanner(options, pluginResponse);
262
- }
263
-
264
-
265
- private void startBLEScanner(@Nullable RequestDeviceOptions options, @NonNull PluginResponse pluginResponse) {
266
- pluginResponseDiscoverDevice = pluginResponse;
267
-
268
- if (!PermissionHelper.hasPermission(this, ACCESS_FINE_LOCATION)) {
269
- permissionCallback = pluginResponse;
270
- PermissionHelper.requestPermission(this, REQUEST_SCAN_PERMISSIONS, ACCESS_FINE_LOCATION);
271
- return;
272
- }
273
-
274
- this.requestDeviceOptions = options;
275
- Log.d(TAG, "startBLEScanner() options: " + options);
276
-
277
- if (this.scanner != null) {
278
- this.scanner.stop();
279
- }
280
- if (options != null) {
281
- this.scanner = new BLEScanner(cordova.getContext(), BluetoothAdapter.getDefaultAdapter(), options.getScanFilters());
282
- } else {
283
- this.scanner = new BLEScanner(cordova.getContext(), BluetoothAdapter.getDefaultAdapter());
284
- }
285
-
286
- if (onDeviceDiscoveredCallback == null) {
287
- this.onDeviceDiscoveredCallback = new IOnDeviceDiscovered<BLEScanner.BLEScanData>() {
288
- @Override
289
- public void onDeviceDiscovered(BLEScanner.BLEScanData device) {
290
- Log.d(TAG, "Device discovered: " + device);
291
- if (pluginResponseDiscoverDevice != null) {
292
- try {
293
- pluginResponseDiscoverDevice.newResult(
294
- JSONBuilder.toJSONObject(device)
295
- );
296
- } catch (Throwable e) {
297
- pluginResponseDiscoverDevice.newResult(BLEComError.internalError(e));
298
- }
299
- } else {
300
- Log.w(TAG, "Discovered device but no listener has been setup");
301
- }
302
- }
303
-
304
- @Override
305
- public void onDeviceLost(BLEScanner.BLEScanData bleScanData) {
306
- Log.w(TAG, "Device lost" + bleScanData);
307
- }
308
-
309
- @Override
310
- public void onScanFailed(Throwable throwable) {
311
- Log.w(TAG, "Scan failed", throwable);
312
- if (pluginResponseDiscoverDevice != null) {
313
- pluginResponseDiscoverDevice.error(BLEComError.internalError(throwable));
314
- }
315
- }
316
-
317
- };
318
- }
319
- this.scanner.setOnDeviceDiscoveredCallback(this.onDeviceDiscoveredCallback);
320
- pluginResponse.newResult("Ok");
321
- this.scanner.start();
322
- }
323
-
324
- @SuppressLint("NewApi")
325
- private void stopScan(PluginResponse pluginResponse) {
326
- pluginResponseDiscoverDevice = null;
327
- if (scanner == null || !scanner.isEnabled()) {
328
- Log.w(TAG, "Scanner is not running");
329
- pluginResponse.success();
330
- return;
331
- }
332
- scanner.stop();
333
- pluginResponse.success();
334
- }
335
-
336
- private void getLastError(PluginResponse pluginResponse) {
337
- throw new Error("Not implemented yet");
338
- }
339
-
340
- private void disconnect(final PluginResponse pluginResponse, String macAddress) {
341
- BLEProtocol peripheral = peripherals.get(macAddress);
342
-
343
- executeAsync(() -> {
344
- try {
345
- peripheral.disconnect();
346
- pluginResponse.success();
347
- } catch (Exception e) {
348
- pluginResponse.error(new BLEComError(BLEComError.Code.DISCONNECTION_ERROR, e));
349
- }
350
- });
351
- }
352
-
353
- private void close(final PluginResponse pluginResponse, String macAddress) {
354
- executeAsync(() -> {
355
- try {
356
- BLEProtocol peripheral = peripherals.getIfExists(macAddress);
357
- if (peripheral != null) {
358
- peripheral.getGatt().close();
359
- peripherals.remove(macAddress);
360
- }
361
- pluginResponse.success();
362
- } catch (Exception e) {
363
- pluginResponse.error(new BLEComError(BLEComError.Code.DISCONNECTION_ERROR, e));
364
- }
365
- });
366
- }
367
-
368
- private void requestMtu(final PluginResponse pluginResponse, String macAddress, int mtu) {
369
- BLEProtocol peripheral = peripherals.get(macAddress);
370
- BluetoothGatt gatt = peripheral.getGatt();
371
- peripheral.requestMtuSize(gatt, mtu, new BLEProtocol.OnGattOperationCallback() {
372
- @Override
373
- public void onResult(int statusCode) {
374
- if (statusCode == BluetoothGatt.GATT_SUCCESS) {
375
- pluginResponse.success();
376
- } else {
377
- pluginResponse.error(BLEComError.statusCodeError(statusCode));
378
- }
379
- }
380
-
381
- @Override
382
- public void onError(Exception ex) {
383
- pluginResponse.error(BLEComError.internalError(ex));
384
- }
385
- });
386
- }
387
-
388
- private void sendLwm2mTapRequest(PluginResponse pluginResponse, String deviceId, byte[] data) throws BLEComError {
389
- if (deviceId == null) {
390
- throw new IllegalArgumentException("Device id must not be null");
391
- }
392
- Log.d(TAG, SEND_REQUEST + " " + deviceId + " 0x" + Helper.ByteArrayToHexString(data));
393
-
394
- BLEProtocol peripheral = peripherals.get(deviceId);
395
- executeAsync(() -> {
396
- try {
397
- byte[] response = peripheral.send(data);
398
- pluginResponse.success(response);
399
- } catch (Exception e) {
400
- pluginResponse.error(BLEComError.lwM2MTapRequestError(e, deviceId, data));
401
- }
402
- });
403
- }
404
-
405
- private void readCharacteristicValue(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
406
- CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
407
- characteristic.read(new CharacteristicAdapter.OnCharacteristicOperationResult() {
408
- @Override
409
- public void onResult(@NotNull BluetoothGattCharacteristic characteristic, int i) {
410
- if (i == BluetoothGatt.GATT_SUCCESS) {
411
- pluginResponse.success(characteristic.getValue());
412
- } else {
413
- pluginResponse.error(BLEComError.statusCodeError(i));
414
- }
415
- }
416
-
417
- @Override
418
- public void onError(@NotNull BluetoothGattCharacteristic characteristic, @NotNull Exception e) {
419
- pluginResponse.error(BLEComError.internalError(e));
420
- }
421
- });
422
- }
423
-
424
- private void writeCharacteristic(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID,
425
- byte[] data, int writeType) throws BLEComError, WritePacketIsTooBigException, CannotWriteCharacteristicException, CharacteristicNotAvailableException, ServiceNotAvailableException {
426
- BLEProtocol peripheral = peripherals.get(macAddress);
427
-
428
- CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
429
- characteristic.write(peripheral.getGatt(), data, writeType, pluginResponse.createOnResultAdapter());
430
- }
431
-
432
- private void startNotification(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
433
- BLEProtocol peripheral = peripherals.get(macAddress);
434
- CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
435
- characteristic.startNotification(peripheral.getGatt(), pluginResponse.createOnResultAdapter());
436
- }
437
-
438
- private void stopNotification(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
439
- BLEProtocol peripheral = peripherals.get(macAddress);
440
- CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
441
- characteristic.stopNotification(peripheral.getGatt(), pluginResponse.createOnResultAdapter());
442
- pluginResponse.success();
443
- }
444
-
445
- private CharacteristicAdapter getDeviceServiceCharacteristicAdapter(String macAddress, UUID serviceUUID, UUID characteristicUUID) throws CharacteristicNotAvailableException, ServiceNotAvailableException {
446
- BLEProtocol peripheral = peripherals.get(macAddress);
447
- return peripheral.getCharacteristicAdapter(serviceUUID, characteristicUUID);
448
- }
449
-
450
- private void executeAsync(Runnable runnable) {
451
- cordova.getThreadPool().execute(runnable);
452
- //runnable.run();
453
- }
454
-
455
- private void connect(PluginResponse pluginResponse, String macAddress) throws BLEComError {
456
- BLEProtocol finalPeripheral = this.getOrCreatePeripheral(macAddress, null);
457
- setConnectionStateCallback(pluginResponse, macAddress);
458
- if (finalPeripheral.isConnected()) {
459
- pluginResponse.newResult("CONNECTED");
460
- return;
461
- }
462
- executeAsync(() -> {
463
- try {
464
- finalPeripheral.bleConnect();
465
- pluginResponse.newResult("CONNECTED");
466
- } catch (Exception e) {
467
- pluginResponse.error(BLEComError.connectionError(e));
468
- }
469
- });
470
- }
471
-
472
- private void connectAndDiscoverTapServices(PluginResponse pluginResponse, String macAddress) throws BLEComError {
473
- BLEProtocol finalPeripheral = this.getOrCreatePeripheral(macAddress, null);
474
- setConnectionStateCallback(pluginResponse, macAddress);
475
- if (finalPeripheral.isConnected()) {
476
- pluginResponse.newResult("CONNECTED");
477
- return;
478
- }
479
- executeAsync(() -> {
480
- try {
481
- finalPeripheral.connect();
482
- pluginResponse.newResult("CONNECTED");
483
- } catch (Exception e) {
484
- pluginResponse.error(BLEComError.connectionError(e));
485
- }
486
- });
487
- }
488
-
489
- private BLEProtocol getOrCreatePeripheral(@NonNull String macAddress, @Nullable Integer initialMTU) throws BLEComError {
490
- BLEProtocol peripheral = peripherals.getIfExists(macAddress);
491
- if (peripheral == null) {
492
- if (!BluetoothAdapter.checkBluetoothAddress(macAddress)) {
493
- throw BLEComError.invalidMacAddress(macAddress);
494
- }
495
- BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress);
496
- peripheral = new BLEProtocol(cordova.getActivity(), device);
497
- peripherals.put(macAddress, peripheral);
498
- if (initialMTU != null) {
499
- peripheral.setMTU(initialMTU);
500
- }
501
- }
502
- return peripheral;
503
- }
504
-
505
- /* @Override */
506
- public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
507
- for (int result : grantResults) {
508
- if (result == PackageManager.PERMISSION_DENIED) {
509
- LOG.d(TAG, "User *rejected* Coarse Location Access");
510
- if (permissionCallback != null) {
511
- this.permissionCallback.error("Location permission not granted.");
512
- }
513
- return;
514
- }
515
- }
516
-
517
- switch (requestCode) {
518
- case REQUEST_SCAN_PERMISSIONS:
519
- LOG.d(TAG, "User granted scan permissions");
520
- this.startScan(this.requestDeviceOptions, pluginResponseDiscoverDevice);
521
- this.permissionCallback = null;
522
- break;
523
- }
524
- }
525
-
526
- @Override
527
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
528
-
529
- if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
530
-
531
- if (resultCode == Activity.RESULT_OK) {
532
- LOG.d(TAG, "User enabled Bluetooth");
533
- if (enableBluetoothCallback != null) {
534
- enableBluetoothCallback.success();
535
- }
536
- } else {
537
- LOG.d(TAG, "User did *NOT* enable Bluetooth");
538
- if (enableBluetoothCallback != null) {
539
- enableBluetoothCallback.error("User did not enable Bluetooth");
540
- }
541
- }
542
-
543
- enableBluetoothCallback = null;
544
- }
545
- }
546
-
547
- private void setConnectionStateCallback(PluginResponse pluginResponse, String macAddress) {
548
- BLEProtocol peripheral = peripherals.getIfExists(macAddress);
549
- if (peripheral == null) {
550
- pluginResponse.error(BLEComError.illegalAction("unregistered peripheral"));
551
- return;
552
- }
553
- peripheral.addOnConnectionStatusChangeListener(
554
- (newConnectionState, oldConnectionState) -> {
555
- LOG.d(TAG, "Old State: " + oldConnectionState + ", New State: " + newConnectionState);
556
- pluginResponse.newResult(newConnectionState.toString());
557
- });
558
- }
1
+ //
2
+ // Copyright 2018 IoTize SAS Inc. Licensed under the MIT license.
3
+ //
4
+ // BLECom.java
5
+ // device-com-ble.cordova BLE Cordova Plugin
6
+ //
7
+
8
+ package com.iotize.plugin.cordova.ble;
9
+
10
+ import static android.Manifest.permission.ACCESS_FINE_LOCATION;
11
+
12
+ import android.annotation.SuppressLint;
13
+ import android.app.Activity;
14
+ import android.bluetooth.BluetoothAdapter;
15
+ import android.bluetooth.BluetoothDevice;
16
+ import android.bluetooth.BluetoothGatt;
17
+ import android.bluetooth.BluetoothGattCharacteristic;
18
+ import android.bluetooth.BluetoothGattService;
19
+ import android.bluetooth.BluetoothManager;
20
+ import android.content.Context;
21
+ import android.content.Intent;
22
+ import android.content.pm.PackageManager;
23
+ import android.util.Log;
24
+
25
+ import com.iotize.android.communication.protocol.ble.BLEProtocol;
26
+ import com.iotize.android.communication.protocol.ble.DeviceManager;
27
+ import com.iotize.android.communication.protocol.ble.characteristics.CharacteristicAdapter;
28
+ import com.iotize.android.communication.protocol.ble.exception.CannotWriteCharacteristicException;
29
+ import com.iotize.android.communication.protocol.ble.exception.CharacteristicNotAvailableException;
30
+ import com.iotize.android.communication.protocol.ble.exception.ServiceNotAvailableException;
31
+ import com.iotize.android.communication.protocol.ble.exception.WritePacketIsTooBigException;
32
+ import com.iotize.android.communication.protocol.ble.scanner.BLEScanner;
33
+ import com.iotize.android.core.util.Helper;
34
+ import com.iotize.android.device.api.device.scanner.IOnDeviceDiscovered;
35
+ import com.iotize.plugin.cordova.ble.models.RequestDeviceOptions;
36
+
37
+ import org.apache.cordova.CallbackContext;
38
+ import org.apache.cordova.CordovaInterface;
39
+ import org.apache.cordova.CordovaPlugin;
40
+ import org.apache.cordova.CordovaWebView;
41
+ import org.apache.cordova.LOG;
42
+ import org.apache.cordova.PermissionHelper;
43
+ import org.jetbrains.annotations.NotNull;
44
+ import org.json.JSONArray;
45
+
46
+ import java.util.List;
47
+ import java.util.UUID;
48
+
49
+ import io.reactivex.annotations.NonNull;
50
+ import io.reactivex.annotations.Nullable;
51
+
52
+ public class BLECom extends CordovaPlugin {
53
+ // actions
54
+ private static final String CONNECT = "connect";
55
+ private static final String CONNECT_AND_DISCOVER_TAP_SERVICES = "connectAndDiscoverTapServices";
56
+ private static final String DISCONNECT = "disConnect";
57
+ private static final String CLOSE = "close";
58
+ private static final String SEND_REQUEST = "sendRequest";
59
+ private static final String GET_LAST_ERROR = "getLastError";
60
+ private static final String START_SCAN = "startScan";
61
+ private static final String STOP_SCAN = "stopScan";
62
+ private static final String IS_ENABLED = "isEnabled";
63
+ private static final String CHECK_AVAILABLE = "checkAvailable";
64
+ private static final String IS_CONNECTED = "isConnected";
65
+ private static final String ENABLE = "enable";
66
+ private static final String REQUEST_MTU = "requestMtu";
67
+
68
+ // New
69
+ private static final String CHARACTERISTIC_READ = "characteristicRead";
70
+ private static final String CHARACTERISTIC_WRITE = "characteristicWrite";
71
+ private static final String CHARACTERISTIC_WRITE_WITHOUT_RESPONSE = "characteristicWriteWithoutResponse";
72
+ private static final String START_NOTIFICATION = "characteristicStartNotification";
73
+ private static final String STOP_NOTIFICATION = "characteristicStopNotification";
74
+ private static final String CHARACTERISTIC_CHANGED = "characteristicChanged";
75
+ private static final String DISCOVER_SERVICES = "discoverServices";
76
+
77
+
78
+ private static final int REQUEST_ENABLE_BLUETOOTH = 1;
79
+ private static final int REQUEST_SCAN_PERMISSIONS = 2;
80
+
81
+ private static final String TAG = "BLECom";
82
+ private DeviceManager<BLEProtocol> peripherals;
83
+ private BluetoothAdapter bluetoothAdapter;
84
+ private PluginResponse enableBluetoothCallback;
85
+ private PluginResponse pluginResponseDiscoverDevice;
86
+ private PluginResponse permissionCallback;
87
+ private BLEScanner scanner;
88
+ private IOnDeviceDiscovered<BLEScanner.BLEScanData> onDeviceDiscoveredCallback;
89
+ @Nullable
90
+ private RequestDeviceOptions requestDeviceOptions;
91
+
92
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
93
+ super.initialize(cordova, webView);
94
+
95
+ this.peripherals = new DeviceManager<>();
96
+ Log.d(TAG, "Initializing IoTizeBLE Plugin");
97
+ }
98
+
99
+ @Override
100
+ public void onStart() {
101
+ super.onStart();
102
+ }
103
+
104
+ @Override
105
+ public void onStop() {
106
+ super.onStop();
107
+ }
108
+
109
+ @SuppressLint("MissingPermission")
110
+ public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) {
111
+
112
+ PluginResponse pluginResponse = new PluginResponse(
113
+ action,
114
+ args,
115
+ callbackContext
116
+ );
117
+ ArgsHelper argsHelper = new ArgsHelper(args);
118
+ try {
119
+ this.setupBluetoothAdapter();
120
+ if (action.equals(STOP_SCAN)) {
121
+ Log.d(TAG, STOP_SCAN);
122
+ this.stopScan(pluginResponse);
123
+ } else if (action.equals(START_SCAN)) {
124
+ Log.d(TAG, START_SCAN);
125
+ RequestDeviceOptions scanOptions = null;
126
+ if (argsHelper.length() > 0) {
127
+ scanOptions = argsHelper.parseJSONAt(0, RequestDeviceOptions.class);
128
+ }
129
+ this.startScan(scanOptions, pluginResponse);
130
+ } else if (action.equals(CONNECT)) {
131
+ Log.d(TAG, CONNECT);
132
+ String macAddress = argsHelper.getString(0);
133
+ this.connect(pluginResponse, macAddress);
134
+ } else if (action.equals(REQUEST_MTU)) {
135
+ Log.d(TAG, REQUEST_MTU);
136
+ String macAddress = argsHelper.getString(0);
137
+ int mtu = argsHelper.getInt(1);
138
+ this.requestMtu(pluginResponse, macAddress, mtu);
139
+ } else if (action.equals(CONNECT_AND_DISCOVER_TAP_SERVICES)) {
140
+ Log.d(TAG, CONNECT_AND_DISCOVER_TAP_SERVICES);
141
+ String macAddress = argsHelper.getString(0);
142
+ this.connectAndDiscoverTapServices(pluginResponse, macAddress);
143
+ } else if (action.equals(DISCONNECT)) {
144
+ Log.d(TAG, DISCONNECT);
145
+ String macAddress = argsHelper.getString(0);
146
+ this.disconnect(pluginResponse, macAddress);
147
+ } else if (action.equals(CLOSE)) {
148
+ Log.d(TAG, CLOSE);
149
+ String macAddress = argsHelper.getString(0);
150
+ this.close(pluginResponse, macAddress);
151
+ } else if (action.equals(GET_LAST_ERROR)) {
152
+ Log.d(TAG, GET_LAST_ERROR);
153
+ this.getLastError(pluginResponse);
154
+ } else if (action.equals(SEND_REQUEST)) {
155
+ String deviceId = argsHelper.getString(0);
156
+ byte[] data = argsHelper.getBytes(1);
157
+ this.sendLwm2mTapRequest(pluginResponse, deviceId, data);
158
+ } else if (action.equals(CHECK_AVAILABLE) || action.equals(IS_ENABLED)) {
159
+ pluginResponse.success(bluetoothAdapter.isEnabled());
160
+ } else if (action.equals(IS_CONNECTED)) {
161
+ String macAddress = argsHelper.getString(0);
162
+ boolean isConnected = peripherals.get(macAddress).isConnected();
163
+ pluginResponse.success(isConnected);
164
+ } else if (action.equals(ENABLE)) {
165
+ if (enableBluetoothCallback != null) {
166
+ Log.w(TAG, "There is already an enable request pending...");
167
+ }
168
+ enableBluetoothCallback = pluginResponse;
169
+ Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
170
+ cordova.startActivityForResult(this, intent, REQUEST_ENABLE_BLUETOOTH);
171
+ } else if (action.equals(CHARACTERISTIC_READ)) {
172
+ String macAddress = argsHelper.getString(0);
173
+ UUID serviceUUID = argsHelper.getUUID(1);
174
+ UUID characteristicUUID = argsHelper.getUUID(2);
175
+ this.readCharacteristicValue(pluginResponse, macAddress, serviceUUID, characteristicUUID);
176
+ } else if (action.equals(CHARACTERISTIC_WRITE)) {
177
+ String macAddress = argsHelper.getString(0);
178
+ UUID serviceUUID = argsHelper.getUUID(1);
179
+ UUID characteristicUUID = argsHelper.getUUID(2);
180
+ byte[] data = argsHelper.getBytes(3);
181
+ this.writeCharacteristic(pluginResponse, macAddress, serviceUUID, characteristicUUID, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
182
+ } else if (action.equals(CHARACTERISTIC_WRITE_WITHOUT_RESPONSE)) {
183
+ String macAddress = argsHelper.getString(0);
184
+ UUID serviceUUID = argsHelper.getUUID(1);
185
+ UUID characteristicUUID = argsHelper.getUUID(2);
186
+ byte[] data = argsHelper.getBytes(3);
187
+ this.writeCharacteristic(pluginResponse, macAddress, serviceUUID, characteristicUUID, data, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
188
+ } else if (action.equals(START_NOTIFICATION)) {
189
+ String macAddress = argsHelper.getString(0);
190
+ UUID serviceUUID = argsHelper.getUUID(1);
191
+ UUID characteristicUUID = argsHelper.getUUID(2);
192
+ this.startNotification(pluginResponse, macAddress, serviceUUID, characteristicUUID);
193
+ } else if (action.equals(STOP_NOTIFICATION)) {
194
+ String macAddress = argsHelper.getString(0);
195
+ UUID serviceUUID = argsHelper.getUUID(1);
196
+ UUID characteristicUUID = argsHelper.getUUID(2);
197
+ this.stopNotification(pluginResponse, macAddress, serviceUUID, characteristicUUID);
198
+ } else if (action.equals(CHARACTERISTIC_CHANGED)) {
199
+ String macAddress = argsHelper.getString(0);
200
+ UUID serviceUUID = argsHelper.getUUID(1);
201
+ UUID characteristicUUID = argsHelper.getUUID(2);
202
+ this.listenToCharacteristicChange(pluginResponse, macAddress, serviceUUID, characteristicUUID);
203
+ } else if (action.equals(DISCOVER_SERVICES)) {
204
+ String macAddress = argsHelper.getString(0);
205
+ this.discoverServices(pluginResponse, macAddress);
206
+ } else {
207
+ throw BLEComError.illegalAction("Illegal action " + action);
208
+ }
209
+ return true;
210
+ } catch (BLEComError err) {
211
+ pluginResponse.error(err);
212
+ return false;
213
+ } catch (Throwable err) {
214
+ pluginResponse.error(BLEComError.internalError(err));
215
+ return false;
216
+ }
217
+
218
+ }
219
+
220
+ private void discoverServices(PluginResponse pluginResponse, String macAddress) throws Exception {
221
+ // TODO async ?
222
+ BLEProtocol peripheral = peripherals.get(macAddress);
223
+ peripheral.discoverServices();
224
+ List<BluetoothGattService> services = peripheral.getGatt().getServices();
225
+ pluginResponse.success(JSONBuilder.toServicesDescription(services));
226
+ }
227
+
228
+ private void listenToCharacteristicChange(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws CharacteristicNotAvailableException, ServiceNotAvailableException {
229
+ CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
230
+ characteristic.onValueChanged(new CharacteristicAdapter.OnCharacteristicValueChangedCallback() {
231
+ @Override
232
+ public void onCharacteristicValueChanged(byte[] bytes) {
233
+ pluginResponse.newResult(bytes);
234
+ }
235
+ });
236
+ }
237
+
238
+ private void setupBluetoothAdapter() throws BLEComError {
239
+ if (bluetoothAdapter == null) {
240
+ Activity activity = cordova.getActivity();
241
+ boolean hardwareSupportsBLE = activity.getApplicationContext()
242
+ .getPackageManager()
243
+ .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
244
+ if (!hardwareSupportsBLE) {
245
+ LOG.w(TAG, "This hardware does not support Bluetooth Low Energy.");
246
+ throw BLEComError.bleNotAvailable();
247
+ }
248
+ BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
249
+ if (bluetoothManager == null) {
250
+ throw BLEComError.bleNotAvailable();
251
+ }
252
+ bluetoothAdapter = bluetoothManager.getAdapter();
253
+ }
254
+
255
+ }
256
+
257
+ private void startScan(@Nullable RequestDeviceOptions options, @NonNull PluginResponse pluginResponse) {
258
+ if (pluginResponseDiscoverDevice != null && pluginResponseDiscoverDevice != pluginResponse) {
259
+ Log.w(TAG, "Scan is already started...");
260
+ }
261
+ this.startBLEScanner(options, pluginResponse);
262
+ }
263
+
264
+
265
+ private void startBLEScanner(@Nullable RequestDeviceOptions options, @NonNull PluginResponse pluginResponse) {
266
+ pluginResponseDiscoverDevice = pluginResponse;
267
+
268
+ if (!PermissionHelper.hasPermission(this, ACCESS_FINE_LOCATION)) {
269
+ permissionCallback = pluginResponse;
270
+ PermissionHelper.requestPermission(this, REQUEST_SCAN_PERMISSIONS, ACCESS_FINE_LOCATION);
271
+ return;
272
+ }
273
+
274
+ this.requestDeviceOptions = options;
275
+ Log.d(TAG, "startBLEScanner() options: " + options);
276
+
277
+ if (this.scanner != null) {
278
+ this.scanner.stop();
279
+ }
280
+ if (options != null) {
281
+ this.scanner = new BLEScanner(cordova.getContext(), BluetoothAdapter.getDefaultAdapter(), options.getScanFilters());
282
+ } else {
283
+ this.scanner = new BLEScanner(cordova.getContext(), BluetoothAdapter.getDefaultAdapter());
284
+ }
285
+
286
+ if (onDeviceDiscoveredCallback == null) {
287
+ this.onDeviceDiscoveredCallback = new IOnDeviceDiscovered<BLEScanner.BLEScanData>() {
288
+ @Override
289
+ public void onDeviceDiscovered(BLEScanner.BLEScanData device) {
290
+ Log.d(TAG, "Device discovered: " + device);
291
+ if (pluginResponseDiscoverDevice != null) {
292
+ try {
293
+ pluginResponseDiscoverDevice.newResult(
294
+ JSONBuilder.toJSONObject(device)
295
+ );
296
+ } catch (Throwable e) {
297
+ pluginResponseDiscoverDevice.newResult(BLEComError.internalError(e));
298
+ }
299
+ } else {
300
+ Log.w(TAG, "Discovered device but no listener has been setup");
301
+ }
302
+ }
303
+
304
+ @Override
305
+ public void onDeviceLost(BLEScanner.BLEScanData bleScanData) {
306
+ Log.w(TAG, "Device lost" + bleScanData);
307
+ }
308
+
309
+ @Override
310
+ public void onScanFailed(Throwable throwable) {
311
+ Log.w(TAG, "Scan failed", throwable);
312
+ if (pluginResponseDiscoverDevice != null) {
313
+ pluginResponseDiscoverDevice.error(BLEComError.internalError(throwable));
314
+ }
315
+ }
316
+
317
+ };
318
+ }
319
+ this.scanner.setOnDeviceDiscoveredCallback(this.onDeviceDiscoveredCallback);
320
+ pluginResponse.newResult("Ok");
321
+ this.scanner.start();
322
+ }
323
+
324
+ @SuppressLint("NewApi")
325
+ private void stopScan(PluginResponse pluginResponse) {
326
+ pluginResponseDiscoverDevice = null;
327
+ if (scanner == null || !scanner.isEnabled()) {
328
+ Log.w(TAG, "Scanner is not running");
329
+ pluginResponse.success();
330
+ return;
331
+ }
332
+ scanner.stop();
333
+ pluginResponse.success();
334
+ }
335
+
336
+ private void getLastError(PluginResponse pluginResponse) {
337
+ throw new Error("Not implemented yet");
338
+ }
339
+
340
+ private void disconnect(final PluginResponse pluginResponse, String macAddress) {
341
+ BLEProtocol peripheral = peripherals.get(macAddress);
342
+
343
+ executeAsync(() -> {
344
+ try {
345
+ peripheral.disconnect();
346
+ pluginResponse.success();
347
+ } catch (Exception e) {
348
+ pluginResponse.error(new BLEComError(BLEComError.Code.DISCONNECTION_ERROR, e));
349
+ }
350
+ });
351
+ }
352
+
353
+ private void close(final PluginResponse pluginResponse, String macAddress) {
354
+ executeAsync(() -> {
355
+ try {
356
+ BLEProtocol peripheral = peripherals.getIfExists(macAddress);
357
+ if (peripheral != null) {
358
+ peripheral.getGatt().close();
359
+ peripherals.remove(macAddress);
360
+ }
361
+ pluginResponse.success();
362
+ } catch (Exception e) {
363
+ pluginResponse.error(new BLEComError(BLEComError.Code.DISCONNECTION_ERROR, e));
364
+ }
365
+ });
366
+ }
367
+
368
+ private void requestMtu(final PluginResponse pluginResponse, String macAddress, int mtu) {
369
+ BLEProtocol peripheral = peripherals.get(macAddress);
370
+ BluetoothGatt gatt = peripheral.getGatt();
371
+ peripheral.requestMtuSize(gatt, mtu, new BLEProtocol.OnGattOperationCallback() {
372
+ @Override
373
+ public void onResult(int statusCode) {
374
+ if (statusCode == BluetoothGatt.GATT_SUCCESS) {
375
+ pluginResponse.success();
376
+ } else {
377
+ pluginResponse.error(BLEComError.statusCodeError(statusCode));
378
+ }
379
+ }
380
+
381
+ @Override
382
+ public void onError(Exception ex) {
383
+ pluginResponse.error(BLEComError.internalError(ex));
384
+ }
385
+ });
386
+ }
387
+
388
+ private void sendLwm2mTapRequest(PluginResponse pluginResponse, String deviceId, byte[] data) throws BLEComError {
389
+ if (deviceId == null) {
390
+ throw new IllegalArgumentException("Device id must not be null");
391
+ }
392
+ Log.d(TAG, SEND_REQUEST + " " + deviceId + " 0x" + Helper.ByteArrayToHexString(data));
393
+
394
+ BLEProtocol peripheral = peripherals.get(deviceId);
395
+ executeAsync(() -> {
396
+ try {
397
+ byte[] response = peripheral.send(data);
398
+ pluginResponse.success(response);
399
+ } catch (Exception e) {
400
+ pluginResponse.error(BLEComError.lwM2MTapRequestError(e, deviceId, data));
401
+ }
402
+ });
403
+ }
404
+
405
+ private void readCharacteristicValue(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
406
+ CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
407
+ characteristic.read(new CharacteristicAdapter.OnCharacteristicOperationResult() {
408
+ @Override
409
+ public void onResult(@NotNull BluetoothGattCharacteristic characteristic, int i) {
410
+ if (i == BluetoothGatt.GATT_SUCCESS) {
411
+ pluginResponse.success(characteristic.getValue());
412
+ } else {
413
+ pluginResponse.error(BLEComError.statusCodeError(i));
414
+ }
415
+ }
416
+
417
+ @Override
418
+ public void onError(@NotNull BluetoothGattCharacteristic characteristic, @NotNull Exception e) {
419
+ pluginResponse.error(BLEComError.internalError(e));
420
+ }
421
+ });
422
+ }
423
+
424
+ private void writeCharacteristic(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID,
425
+ byte[] data, int writeType) throws BLEComError, WritePacketIsTooBigException, CannotWriteCharacteristicException, CharacteristicNotAvailableException, ServiceNotAvailableException {
426
+ BLEProtocol peripheral = peripherals.get(macAddress);
427
+
428
+ CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
429
+ characteristic.write(peripheral.getGatt(), data, writeType, pluginResponse.createOnResultAdapter());
430
+ }
431
+
432
+ private void startNotification(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
433
+ BLEProtocol peripheral = peripherals.get(macAddress);
434
+ CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
435
+ characteristic.startNotification(peripheral.getGatt(), pluginResponse.createOnResultAdapter());
436
+ }
437
+
438
+ private void stopNotification(PluginResponse pluginResponse, String macAddress, UUID serviceUUID, UUID characteristicUUID) throws BLEComError, CharacteristicNotAvailableException, ServiceNotAvailableException {
439
+ BLEProtocol peripheral = peripherals.get(macAddress);
440
+ CharacteristicAdapter characteristic = this.getDeviceServiceCharacteristicAdapter(macAddress, serviceUUID, characteristicUUID);
441
+ characteristic.stopNotification(peripheral.getGatt(), pluginResponse.createOnResultAdapter());
442
+ pluginResponse.success();
443
+ }
444
+
445
+ private CharacteristicAdapter getDeviceServiceCharacteristicAdapter(String macAddress, UUID serviceUUID, UUID characteristicUUID) throws CharacteristicNotAvailableException, ServiceNotAvailableException {
446
+ BLEProtocol peripheral = peripherals.get(macAddress);
447
+ return peripheral.getCharacteristicAdapter(serviceUUID, characteristicUUID);
448
+ }
449
+
450
+ private void executeAsync(Runnable runnable) {
451
+ cordova.getThreadPool().execute(runnable);
452
+ //runnable.run();
453
+ }
454
+
455
+ private void connect(PluginResponse pluginResponse, String macAddress) throws BLEComError {
456
+ BLEProtocol finalPeripheral = this.getOrCreatePeripheral(macAddress, null);
457
+ setConnectionStateCallback(pluginResponse, macAddress);
458
+ if (finalPeripheral.isConnected()) {
459
+ pluginResponse.newResult("CONNECTED");
460
+ return;
461
+ }
462
+ executeAsync(() -> {
463
+ try {
464
+ finalPeripheral.bleConnect();
465
+ pluginResponse.newResult("CONNECTED");
466
+ } catch (Exception e) {
467
+ pluginResponse.error(BLEComError.connectionError(e));
468
+ }
469
+ });
470
+ }
471
+
472
+ private void connectAndDiscoverTapServices(PluginResponse pluginResponse, String macAddress) throws BLEComError {
473
+ BLEProtocol finalPeripheral = this.getOrCreatePeripheral(macAddress, null);
474
+ setConnectionStateCallback(pluginResponse, macAddress);
475
+ if (finalPeripheral.isConnected()) {
476
+ pluginResponse.newResult("CONNECTED");
477
+ return;
478
+ }
479
+ executeAsync(() -> {
480
+ try {
481
+ finalPeripheral.connect();
482
+ pluginResponse.newResult("CONNECTED");
483
+ } catch (Exception e) {
484
+ pluginResponse.error(BLEComError.connectionError(e));
485
+ }
486
+ });
487
+ }
488
+
489
+ private BLEProtocol getOrCreatePeripheral(@NonNull String macAddress, @Nullable Integer initialMTU) throws BLEComError {
490
+ BLEProtocol peripheral = peripherals.getIfExists(macAddress);
491
+ if (peripheral == null) {
492
+ if (!BluetoothAdapter.checkBluetoothAddress(macAddress)) {
493
+ throw BLEComError.invalidMacAddress(macAddress);
494
+ }
495
+ BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress);
496
+ peripheral = new BLEProtocol(cordova.getActivity(), device);
497
+ peripheral.setConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_BALANCED);
498
+ peripherals.put(macAddress, peripheral);
499
+ if (initialMTU != null) {
500
+ peripheral.setMTU(initialMTU);
501
+ }
502
+ }
503
+ return peripheral;
504
+ }
505
+
506
+ /* @Override */
507
+ public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
508
+ for (int result : grantResults) {
509
+ if (result == PackageManager.PERMISSION_DENIED) {
510
+ LOG.d(TAG, "User *rejected* Coarse Location Access");
511
+ if (permissionCallback != null) {
512
+ this.permissionCallback.error("Location permission not granted.");
513
+ }
514
+ return;
515
+ }
516
+ }
517
+
518
+ switch (requestCode) {
519
+ case REQUEST_SCAN_PERMISSIONS:
520
+ LOG.d(TAG, "User granted scan permissions");
521
+ this.startScan(this.requestDeviceOptions, pluginResponseDiscoverDevice);
522
+ this.permissionCallback = null;
523
+ break;
524
+ }
525
+ }
526
+
527
+ @Override
528
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
529
+
530
+ if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
531
+
532
+ if (resultCode == Activity.RESULT_OK) {
533
+ LOG.d(TAG, "User enabled Bluetooth");
534
+ if (enableBluetoothCallback != null) {
535
+ enableBluetoothCallback.success();
536
+ }
537
+ } else {
538
+ LOG.d(TAG, "User did *NOT* enable Bluetooth");
539
+ if (enableBluetoothCallback != null) {
540
+ enableBluetoothCallback.error("User did not enable Bluetooth");
541
+ }
542
+ }
543
+
544
+ enableBluetoothCallback = null;
545
+ }
546
+ }
547
+
548
+ private void setConnectionStateCallback(PluginResponse pluginResponse, String macAddress) {
549
+ BLEProtocol peripheral = peripherals.getIfExists(macAddress);
550
+ if (peripheral == null) {
551
+ pluginResponse.error(BLEComError.illegalAction("unregistered peripheral"));
552
+ return;
553
+ }
554
+ peripheral.addOnConnectionStatusChangeListener(
555
+ (newConnectionState, oldConnectionState) -> {
556
+ LOG.d(TAG, "Old State: " + oldConnectionState + ", New State: " + newConnectionState);
557
+ pluginResponse.newResult(newConnectionState.toString());
558
+ });
559
+ }
559
560
  }