react-native-ble-manager 8.5.0 → 8.7.0
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/BleManager.js +10 -2
- package/README.md +6 -5
- package/android/src/main/java/it/innove/BleManager.java +2 -3
- package/android/src/main/java/it/innove/LegacyScanManager.java +85 -84
- package/android/src/main/java/it/innove/LollipopScanManager.java +71 -48
- package/android/src/main/java/it/innove/Peripheral.java +448 -441
- package/index.d.ts +1 -0
- package/package.json +1 -1
package/BleManager.js
CHANGED
|
@@ -250,7 +250,7 @@ class BleManager {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// (ANDROID) Match as many advertisement per filter as hw could allow
|
|
253
|
-
//
|
|
253
|
+
// depends on current capability and availability of the resources in hw.
|
|
254
254
|
if (scanningOptions.numberOfMatches == null) {
|
|
255
255
|
scanningOptions.numberOfMatches = 3;
|
|
256
256
|
}
|
|
@@ -260,11 +260,19 @@ class BleManager {
|
|
|
260
260
|
scanningOptions.matchMode = 1;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
// (ANDROID) Defaults to SCAN_MODE_LOW_POWER
|
|
263
|
+
// (ANDROID) Defaults to SCAN_MODE_LOW_POWER
|
|
264
264
|
if (scanningOptions.scanMode == null) {
|
|
265
265
|
scanningOptions.scanMode = 0;
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
// (ANDROID) Defaults to CALLBACK_TYPE_ALL_MATCHES
|
|
269
|
+
// WARN: sometimes, setting a scanSetting instead of leaving it untouched might result in unexpected behaviors.
|
|
270
|
+
// https://github.com/dariuszseweryn/RxAndroidBle/issues/462
|
|
271
|
+
if (scanningOptions.callbackType == null) {
|
|
272
|
+
scanningOptions.callbackType = 1;
|
|
273
|
+
}
|
|
267
274
|
|
|
275
|
+
// (ANDROID) Defaults to 0ms (report results immediately).
|
|
268
276
|
if (scanningOptions.reportDelay == null) {
|
|
269
277
|
scanningOptions.reportDelay = 0;
|
|
270
278
|
}
|
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ BleManager.start({ showAlert: false }).then(() => {
|
|
|
122
122
|
|
|
123
123
|
### scan(serviceUUIDs, seconds, allowDuplicates, scanningOptions)
|
|
124
124
|
|
|
125
|
-
Scan for
|
|
125
|
+
Scan for available peripherals.
|
|
126
126
|
Returns a `Promise` object.
|
|
127
127
|
|
|
128
128
|
**Arguments**
|
|
@@ -131,10 +131,11 @@ Returns a `Promise` object.
|
|
|
131
131
|
- `seconds` - `Integer` - the amount of seconds to scan.
|
|
132
132
|
- `allowDuplicates` - `Boolean` - [iOS only] allow duplicates in device scanning
|
|
133
133
|
- `scanningOptions` - `JSON` - [Android only] after Android 5.0, user can control specific ble scan behaviors:
|
|
134
|
-
- `numberOfMatches` - `Number` - [Android only] corresponding to [`setNumOfMatches`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setNumOfMatches(int)>)
|
|
135
|
-
- `matchMode` - `Number` - [Android only] corresponding to [`setMatchMode`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setMatchMode(int)>)
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
134
|
+
- `numberOfMatches` - `Number` - [Android only] corresponding to [`setNumOfMatches`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setNumOfMatches(int)>). Defaults to `ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT`.
|
|
135
|
+
- `matchMode` - `Number` - [Android only] corresponding to [`setMatchMode`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setMatchMode(int)>). Defaults to `ScanSettings.MATCH_MODE_AGGRESSIVE`.
|
|
136
|
+
- `callbackType` - `Number` - [Android only] corresponding to [`setCallbackType`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setCallbackType(int)>). Defaults `ScanSettings.CALLBACK_TYPE_ALL_MATCHES`.
|
|
137
|
+
- `scanMode` - `Number` - [Android only] corresponding to [`setScanMode`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setScanMode(int)>). Defaults to `ScanSettings.SCAN_MODE_LOW_POWER`.
|
|
138
|
+
- `reportDelay` - `Number` - [Android only] corresponding to [`setReportDelay`](<https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder.html#setReportDelay(long)>). Defaults to `0ms`.
|
|
138
139
|
- `phy` - `Number` - [Android only] corresponding to [`setPhy`](https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder#setPhy(int))
|
|
139
140
|
- `legacy` - `Boolean` - [Android only] corresponding to [`setLegacy`](https://developer.android.com/reference/android/bluetooth/le/ScanSettings.Builder#setLegacy(boolean))
|
|
140
141
|
|
|
@@ -291,8 +291,7 @@ class BleManager extends ReactContextBaseJavaModule {
|
|
|
291
291
|
|
|
292
292
|
Peripheral peripheral = peripherals.get(peripheralUUID);
|
|
293
293
|
if (peripheral != null) {
|
|
294
|
-
peripheral.disconnect(force);
|
|
295
|
-
callback.invoke();
|
|
294
|
+
peripheral.disconnect(callback, force);
|
|
296
295
|
} else
|
|
297
296
|
callback.invoke("Peripheral not found");
|
|
298
297
|
}
|
|
@@ -590,7 +589,7 @@ class BleManager extends ReactContextBaseJavaModule {
|
|
|
590
589
|
synchronized (peripherals) {
|
|
591
590
|
for (Peripheral peripheral : peripherals.values()) {
|
|
592
591
|
if (peripheral.isConnected()) {
|
|
593
|
-
peripheral.disconnect(true);
|
|
592
|
+
peripheral.disconnect(null,true);
|
|
594
593
|
}
|
|
595
594
|
}
|
|
596
595
|
}
|
|
@@ -3,94 +3,95 @@ package it.innove;
|
|
|
3
3
|
import android.bluetooth.BluetoothAdapter;
|
|
4
4
|
import android.bluetooth.BluetoothDevice;
|
|
5
5
|
import android.util.Log;
|
|
6
|
+
|
|
6
7
|
import com.facebook.react.bridge.*;
|
|
7
8
|
|
|
8
9
|
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
9
10
|
|
|
10
11
|
public class LegacyScanManager extends ScanManager {
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
13
|
+
public LegacyScanManager(ReactApplicationContext reactContext, BleManager bleManager) {
|
|
14
|
+
super(reactContext, bleManager);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Override
|
|
18
|
+
public void stopScan(Callback callback) {
|
|
19
|
+
// update scanSessionId to prevent stopping next scan by running timeout thread
|
|
20
|
+
scanSessionId.incrementAndGet();
|
|
21
|
+
|
|
22
|
+
getBluetoothAdapter().stopLeScan(mLeScanCallback);
|
|
23
|
+
callback.invoke();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private BluetoothAdapter.LeScanCallback mLeScanCallback =
|
|
27
|
+
new BluetoothAdapter.LeScanCallback() {
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
public void onLeScan(final BluetoothDevice device, final int rssi,
|
|
31
|
+
final byte[] scanRecord) {
|
|
32
|
+
runOnUiThread(new Runnable() {
|
|
33
|
+
@Override
|
|
34
|
+
public void run() {
|
|
35
|
+
Log.i(bleManager.LOG_TAG, "DiscoverPeripheral: " + device.getName());
|
|
36
|
+
|
|
37
|
+
Peripheral peripheral = bleManager.getPeripheral(device);
|
|
38
|
+
if (peripheral == null) {
|
|
39
|
+
peripheral = new Peripheral(device, rssi, scanRecord, bleManager.getReactContext());
|
|
40
|
+
} else {
|
|
41
|
+
peripheral.updateData(scanRecord);
|
|
42
|
+
peripheral.updateRssi(rssi);
|
|
43
|
+
}
|
|
44
|
+
bleManager.savePeripheral(peripheral);
|
|
45
|
+
|
|
46
|
+
WritableMap map = peripheral.asWritableMap();
|
|
47
|
+
bleManager.sendEvent("BleManagerDiscoverPeripheral", map);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
public void scan(ReadableArray serviceUUIDs, final int scanSeconds, ReadableMap options, Callback callback) {
|
|
57
|
+
if (serviceUUIDs.size() > 0) {
|
|
58
|
+
Log.d(bleManager.LOG_TAG, "Filter is not working in pre-lollipop devices");
|
|
59
|
+
}
|
|
60
|
+
getBluetoothAdapter().startLeScan(mLeScanCallback);
|
|
61
|
+
|
|
62
|
+
if (scanSeconds > 0) {
|
|
63
|
+
Thread thread = new Thread() {
|
|
64
|
+
private int currentScanSession = scanSessionId.incrementAndGet();
|
|
65
|
+
|
|
66
|
+
@Override
|
|
67
|
+
public void run() {
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
Thread.sleep(scanSeconds * 1000);
|
|
71
|
+
} catch (InterruptedException ignored) {
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
runOnUiThread(new Runnable() {
|
|
75
|
+
@Override
|
|
76
|
+
public void run() {
|
|
77
|
+
BluetoothAdapter btAdapter = getBluetoothAdapter();
|
|
78
|
+
// check current scan session was not stopped
|
|
79
|
+
if (scanSessionId.intValue() == currentScanSession) {
|
|
80
|
+
if (btAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
|
81
|
+
btAdapter.stopLeScan(mLeScanCallback);
|
|
82
|
+
}
|
|
83
|
+
WritableMap map = Arguments.createMap();
|
|
84
|
+
map.putInt("status", 0);
|
|
85
|
+
bleManager.sendEvent("BleManagerStopScan", map);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
};
|
|
93
|
+
thread.start();
|
|
94
|
+
}
|
|
95
|
+
callback.invoke();
|
|
96
|
+
}
|
|
96
97
|
}
|
|
@@ -1,52 +1,63 @@
|
|
|
1
1
|
package it.innove;
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
5
|
+
|
|
6
|
+
import android.Manifest;
|
|
4
7
|
import android.bluetooth.BluetoothAdapter;
|
|
5
8
|
import android.bluetooth.BluetoothDevice;
|
|
6
9
|
import android.bluetooth.le.ScanCallback;
|
|
7
10
|
import android.bluetooth.le.ScanFilter;
|
|
11
|
+
import android.bluetooth.le.ScanRecord;
|
|
8
12
|
import android.bluetooth.le.ScanResult;
|
|
9
13
|
import android.bluetooth.le.ScanSettings;
|
|
14
|
+
import android.content.pm.PackageManager;
|
|
10
15
|
import android.os.Build;
|
|
11
16
|
import android.os.ParcelUuid;
|
|
12
|
-
import androidx.annotation.RequiresApi;
|
|
13
17
|
import android.util.Log;
|
|
14
|
-
|
|
18
|
+
|
|
19
|
+
import androidx.annotation.RequiresApi;
|
|
20
|
+
import androidx.core.app.ActivityCompat;
|
|
21
|
+
|
|
22
|
+
import com.facebook.react.bridge.Arguments;
|
|
23
|
+
import com.facebook.react.bridge.Callback;
|
|
24
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
25
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
26
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
27
|
+
import com.facebook.react.bridge.WritableMap;
|
|
15
28
|
|
|
16
29
|
import java.util.ArrayList;
|
|
17
30
|
import java.util.List;
|
|
18
31
|
|
|
19
|
-
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
20
|
-
|
|
21
32
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
22
33
|
public class LollipopScanManager extends ScanManager {
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
public LollipopScanManager(ReactApplicationContext reactContext, BleManager bleManager) {
|
|
36
|
+
super(reactContext, bleManager);
|
|
37
|
+
}
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
@Override
|
|
40
|
+
public void stopScan(Callback callback) {
|
|
41
|
+
// update scanSessionId to prevent stopping next scan by running timeout thread
|
|
42
|
+
scanSessionId.incrementAndGet();
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
getBluetoothAdapter().getBluetoothLeScanner().stopScan(mScanCallback);
|
|
45
|
+
callback.invoke();
|
|
46
|
+
}
|
|
36
47
|
|
|
37
48
|
@Override
|
|
38
|
-
public void scan(ReadableArray serviceUUIDs, final int scanSeconds, ReadableMap options,
|
|
49
|
+
public void scan(ReadableArray serviceUUIDs, final int scanSeconds, ReadableMap options, Callback callback) {
|
|
39
50
|
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
|
|
40
51
|
List<ScanFilter> filters = new ArrayList<>();
|
|
41
52
|
|
|
42
53
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && options.hasKey("legacy")) {
|
|
43
54
|
scanSettingsBuilder.setLegacy(options.getBoolean("legacy"));
|
|
44
55
|
}
|
|
45
|
-
|
|
56
|
+
|
|
46
57
|
if (options.hasKey("scanMode")) {
|
|
47
58
|
scanSettingsBuilder.setScanMode(options.getInt("scanMode"));
|
|
48
59
|
}
|
|
49
|
-
|
|
60
|
+
|
|
50
61
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
51
62
|
if (options.hasKey("numberOfMatches")) {
|
|
52
63
|
scanSettingsBuilder.setNumOfMatches(options.getInt("numberOfMatches"));
|
|
@@ -54,6 +65,9 @@ public class LollipopScanManager extends ScanManager {
|
|
|
54
65
|
if (options.hasKey("matchMode")) {
|
|
55
66
|
scanSettingsBuilder.setMatchMode(options.getInt("matchMode"));
|
|
56
67
|
}
|
|
68
|
+
if (options.hasKey("callbackType")) {
|
|
69
|
+
scanSettingsBuilder.setCallbackType(options.getInt("callbackType"));
|
|
70
|
+
}
|
|
57
71
|
}
|
|
58
72
|
|
|
59
73
|
if (options.hasKey("reportDelay")) {
|
|
@@ -69,60 +83,69 @@ public class LollipopScanManager extends ScanManager {
|
|
|
69
83
|
scanSettingsBuilder.setPhy(BluetoothDevice.PHY_LE_2M);
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
|
-
|
|
86
|
+
|
|
73
87
|
if (serviceUUIDs.size() > 0) {
|
|
74
|
-
for(int i = 0; i < serviceUUIDs.size(); i++){
|
|
75
|
-
|
|
88
|
+
for (int i = 0; i < serviceUUIDs.size(); i++) {
|
|
89
|
+
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(UUIDHelper.uuidFromString(serviceUUIDs.getString(i)))).build();
|
|
76
90
|
filters.add(filter);
|
|
77
91
|
Log.d(bleManager.LOG_TAG, "Filter service: " + serviceUUIDs.getString(i));
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
|
-
|
|
94
|
+
|
|
81
95
|
getBluetoothAdapter().getBluetoothLeScanner().startScan(filters, scanSettingsBuilder.build(), mScanCallback);
|
|
82
96
|
if (scanSeconds > 0) {
|
|
83
97
|
Thread thread = new Thread() {
|
|
84
98
|
private int currentScanSession = scanSessionId.incrementAndGet();
|
|
85
|
-
|
|
99
|
+
|
|
86
100
|
@Override
|
|
87
101
|
public void run() {
|
|
88
|
-
|
|
102
|
+
|
|
89
103
|
try {
|
|
90
104
|
Thread.sleep(scanSeconds * 1000);
|
|
91
105
|
} catch (InterruptedException ignored) {
|
|
92
106
|
}
|
|
93
|
-
|
|
107
|
+
|
|
94
108
|
runOnUiThread(new Runnable() {
|
|
95
109
|
@Override
|
|
96
110
|
public void run() {
|
|
97
111
|
BluetoothAdapter btAdapter = getBluetoothAdapter();
|
|
98
112
|
// check current scan session was not stopped
|
|
99
113
|
if (scanSessionId.intValue() == currentScanSession) {
|
|
100
|
-
if(btAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
|
114
|
+
if (btAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
|
101
115
|
btAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
|
|
102
116
|
}
|
|
103
117
|
WritableMap map = Arguments.createMap();
|
|
104
|
-
|
|
118
|
+
map.putInt("status", 10);
|
|
105
119
|
bleManager.sendEvent("BleManagerStopScan", map);
|
|
106
120
|
}
|
|
107
121
|
}
|
|
108
122
|
});
|
|
109
|
-
|
|
123
|
+
|
|
110
124
|
}
|
|
111
|
-
|
|
125
|
+
|
|
112
126
|
};
|
|
113
127
|
thread.start();
|
|
114
128
|
}
|
|
115
129
|
callback.invoke();
|
|
116
130
|
}
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
private ScanCallback mScanCallback = new ScanCallback() {
|
|
133
|
+
@Override
|
|
134
|
+
public void onScanResult(final int callbackType, final ScanResult result) {
|
|
121
135
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
runOnUiThread(new Runnable() {
|
|
137
|
+
@Override
|
|
138
|
+
public void run() {
|
|
139
|
+
String info;
|
|
140
|
+
ScanRecord record = result.getScanRecord();
|
|
141
|
+
if (record != null)
|
|
142
|
+
info = record.getDeviceName();
|
|
143
|
+
else if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED)
|
|
144
|
+
info = result.getDevice().getName();
|
|
145
|
+
else
|
|
146
|
+
info = result.toString();
|
|
147
|
+
|
|
148
|
+
Log.i(bleManager.LOG_TAG, "DiscoverPeripheral: " + info);
|
|
126
149
|
|
|
127
150
|
LollipopPeripheral peripheral = (LollipopPeripheral) bleManager.getPeripheral(result.getDevice());
|
|
128
151
|
if (peripheral == null) {
|
|
@@ -133,21 +156,21 @@ public class LollipopScanManager extends ScanManager {
|
|
|
133
156
|
}
|
|
134
157
|
bleManager.savePeripheral(peripheral);
|
|
135
158
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
159
|
+
WritableMap map = peripheral.asWritableMap();
|
|
160
|
+
bleManager.sendEvent("BleManagerDiscoverPeripheral", map);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
141
164
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
@Override
|
|
166
|
+
public void onBatchScanResults(final List<ScanResult> results) {
|
|
167
|
+
}
|
|
145
168
|
|
|
146
|
-
|
|
147
|
-
|
|
169
|
+
@Override
|
|
170
|
+
public void onScanFailed(final int errorCode) {
|
|
148
171
|
WritableMap map = Arguments.createMap();
|
|
149
|
-
|
|
172
|
+
map.putInt("status", errorCode);
|
|
150
173
|
bleManager.sendEvent("BleManagerStopScan", map);
|
|
151
|
-
|
|
152
|
-
|
|
174
|
+
}
|
|
175
|
+
};
|
|
153
176
|
}
|