123 | * Perform unpaired through reflection
124 | *
125 | * @param bluetoothDevice
126 | * @return true or false
127 | */
128 | public boolean disBoundDevice(BluetoothDevice bluetoothDevice) {
129 | if (bluetoothDevice == null) {
130 | return false;
131 | }
132 | try {
133 | Method removeBondMethod = BluetoothDevice.class.getMethod("removeBond");
134 | Boolean returnValue = (Boolean) removeBondMethod.invoke(bluetoothDevice);
135 | return returnValue.booleanValue();
136 | } catch (NoSuchMethodException e) {
137 | e.printStackTrace();
138 | } catch (IllegalAccessException e) {
139 | e.printStackTrace();
140 | } catch (InvocationTargetException e) {
141 | e.printStackTrace();
142 | }
143 | return false;
144 | }
145 |
146 |
147 | public void scan(@BluetoothType int scanType, int timeUse, ScanCallBack scanCallBack) {
148 | if (scanner != null) {
149 | scanner.scan(scanType, timeUse, scanCallBack);
150 | }
151 | }
152 |
153 | /**
154 | * @param scanType
155 | * @param filter Accurate or fuzzy matching scanning according to the device name
156 | * @param scanCallBack
157 | */
158 | public void scan(@BluetoothType int scanType, ScanFilter filter, ScanCallBack scanCallBack) {
159 | if (scanner != null) {
160 | scanner.setFilter(filter);
161 | scanner.scan(scanType, scanCallBack);
162 | }
163 | }
164 |
165 | /**
166 | * @param scanType
167 | * @param timeUse
168 | * @param filter Accurate or fuzzy matching scanning according to the device name
169 | * @param scanCallBack
170 | */
171 | public void scan(@BluetoothType int scanType, int timeUse, ScanFilter filter, ScanCallBack scanCallBack) {
172 | if (scanner != null) {
173 | scanner.setFilter(filter);
174 | scanner.scan(scanType, timeUse, scanCallBack);
175 | }
176 | }
177 |
178 | public Scanner scanner() {
179 | checkIfEnableBluetoothFirst();
180 | return scanner;
181 | }
182 |
183 | public synchronized void cancelScan() {
184 | if (scanner != null) {
185 | scanner.stopScan();
186 | }
187 | }
188 |
189 |
190 | public synchronized void destroyChannel() {
191 | if (sender != null) {
192 | sender.destroyChannel();
193 | }
194 | }
195 |
196 | private void resetCallBack() {
197 | if (sender != null) {
198 | sender.resetCallBack();
199 | }
200 | if (scanner != null) {
201 | scanner.resetCallBack();
202 | }
203 | }
204 |
205 |
206 | public void connect(com.hjy.bluetooth.entity.BluetoothDevice bluetoothDevice, ConnectCallBack connectCallBack) {
207 | if (connector != null) {
208 | connector.connect(bluetoothDevice, connectCallBack);
209 | }
210 | }
211 |
212 | public void connect(com.hjy.bluetooth.entity.BluetoothDevice bluetoothDevice, ConnectCallBack connectCallBack, BleNotifyCallBack bleNotifyCallBack) {
213 | if (connector != null) {
214 | connector.connect(bluetoothDevice, connectCallBack, bleNotifyCallBack);
215 | }
216 | }
217 |
218 | public void connect(com.hjy.bluetooth.entity.BluetoothDevice bluetoothDevice, ClassicBluetoothPairCallBack classicBluetoothPairCallBack, ConnectCallBack connectCallBack) {
219 | if (connector != null) {
220 | connector.connect(bluetoothDevice, classicBluetoothPairCallBack, connectCallBack);
221 | }
222 | }
223 |
224 | public Connector connector() {
225 | checkIfEnableBluetoothFirst();
226 | return connector;
227 | }
228 |
229 | public void send(byte[] cmd, SendCallBack sendCallBack) {
230 | if (sender != null) {
231 | sender.send(cmd, sendCallBack);
232 | }
233 | }
234 |
235 | public void readBleCharacteristic(String serviceUUID, String characteristicUUID, SendCallBack sendCallBack) {
236 | if (sender != null) {
237 | sender.readCharacteristic(serviceUUID, characteristicUUID, sendCallBack);
238 | }
239 | }
240 |
241 | public Sender sender() {
242 | checkIfEnableBluetoothFirst();
243 | return sender;
244 | }
245 |
246 | public Receiver setReceiver(ReceiveCallBack receiveCallBack) {
247 | if (receiver != null) {
248 | receiver.setReceiveCallBack(receiveCallBack);
249 | }
250 | return receiver;
251 | }
252 |
253 | public Receiver receiver() {
254 | checkIfEnableBluetoothFirst();
255 | return receiver;
256 | }
257 |
258 | private void checkIfEnableBluetoothFirst() {
259 | if (mAdapter == null || !mAdapter.isEnabled()) {
260 | throw new RuntimeException("You must call enableBluetooth() first.");
261 | }
262 | }
263 |
264 | public boolean isConnected() {
265 | return isConnected;
266 | }
267 |
268 | public void setConnected(boolean connected) {
269 | isConnected = connected;
270 | }
271 |
272 | public int getConnectTimeOut() {
273 | return connectTimeOut;
274 | }
275 |
276 | public HBluetooth setConnectTimeOut(int connectTimeOut) {
277 | this.connectTimeOut = connectTimeOut;
278 | return this;
279 | }
280 |
281 | public HBluetooth openReconnect(int reconnectTryTimes, int reconnectInterval) {
282 | this.reconnectTryTimes = reconnectTryTimes > 6 ? 6 : reconnectTryTimes;
283 | this.reconnectInterval = reconnectInterval < 0 ? 0 : reconnectInterval;
284 | return this;
285 | }
286 |
287 | public int getReconnectTryTimes() {
288 | return reconnectTryTimes;
289 | }
290 |
291 | public int getReconnectInterval() {
292 | return reconnectInterval;
293 | }
294 |
295 | public void setUserActiveDisconnect(boolean userActiveDisconnect) {
296 | isUserActiveDisconnect = userActiveDisconnect;
297 | }
298 |
299 | /**
300 | * Release operation when automatic disconnection.
301 | * Please call the 'release()' method to instead when you disconnect manually,for example, through the button.
302 | */
303 | public synchronized void releaseIgnoreActiveDisconnect() {
304 | cancelScan();
305 | destroyChannel();
306 | resetCallBack();
307 | }
308 |
309 | public boolean isUserActiveDisconnect() {
310 | return isUserActiveDisconnect;
311 | }
312 |
313 | public BleConfig getBleConfig() {
314 | return mBleConfig;
315 | }
316 |
317 | public void setBleConfig(BleConfig bleConfig) {
318 | mBleConfig = bleConfig;
319 | }
320 |
321 | public ClassicBluetoothConfig getClassicBluetoothConfig() {
322 | return mClassicBluetoothConfig;
323 | }
324 |
325 | public void setClassicBluetoothConfig(ClassicBluetoothConfig classicBluetoothConfig) {
326 | mClassicBluetoothConfig = classicBluetoothConfig;
327 | }
328 |
329 | /**
330 | * The config only for classic bluetooth
331 | */
332 | public static class ClassicBluetoothConfig {
333 | private int pairMode = ClassicBluetoothPairMode.JUST_WORK;
334 | private String pinCode = "1234";
335 |
336 | /**
337 | * Default mode:JUST_WORK.
338 | * JUST_WORK: Connect without pair
339 | * PIN_CODE: If the device is not bound, it will be paired before connecting
340 | *
341 | * @param pairMode
342 | * @return
343 | */
344 | public ClassicBluetoothConfig setPairMode(@PairMode int pairMode) {
345 | this.pairMode = pairMode;
346 | return this;
347 | }
348 |
349 | public int getPairMode() {
350 | return pairMode;
351 | }
352 |
353 | public String getPinCode() {
354 | return pinCode;
355 | }
356 |
357 | /**
358 | * Only the paired mode of PIN_CODE need to call this method
359 | *
360 | * @param pinCode
361 | * @return
362 | */
363 | public ClassicBluetoothConfig setPinCode(String pinCode) {
364 | this.pinCode = pinCode;
365 | return this;
366 | }
367 | }
368 |
369 |
370 | /**
371 | * The config only for ble
372 | */
373 | public static class BleConfig {
374 | private String serviceUUID, writeCharacteristicUUID, notifyCharacteristicUUID;
375 | private boolean useCharacteristicDescriptor;
376 | private int mtuSize;
377 | private int sendTimeInterval = 20;
378 | private int eachSplitPacketLen = 20;
379 | private int notifyDelay = 200;
380 | private UUID[] scanFilterServerUUIDs;
381 | private boolean splitPacketToSendWhenCmdLenBeyond, autoConnect;
382 | private boolean liveUpdateScannedDeviceName;
383 | private BleMtuChangedCallback mBleMtuChangedCallback;
384 |
385 | public BleConfig withServiceUUID(String serviceUUID) {
386 | this.serviceUUID = serviceUUID;
387 | return this;
388 | }
389 |
390 | public BleConfig withWriteCharacteristicUUID(String writeCharacteristicUUID) {
391 | this.writeCharacteristicUUID = writeCharacteristicUUID;
392 | return this;
393 | }
394 |
395 | public BleConfig withNotifyCharacteristicUUID(String notifyCharacteristicUUID) {
396 | this.notifyCharacteristicUUID = notifyCharacteristicUUID;
397 | return this;
398 | }
399 |
400 | public BleConfig useCharacteristicDescriptor(boolean useCharacteristicDescriptor) {
401 | this.useCharacteristicDescriptor = useCharacteristicDescriptor;
402 | return this;
403 | }
404 |
405 | public UUID[] getScanFilterServerUUIDs() {
406 | return scanFilterServerUUIDs;
407 | }
408 |
409 | public BleConfig setScanFilterServerUUIDs(UUID[] scanFilterServerUUIDs) {
410 | this.scanFilterServerUUIDs = scanFilterServerUUIDs;
411 | return this;
412 | }
413 |
414 | /**
415 | * Default value is false
416 | * Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true).
417 | *
418 | * @param autoConnect
419 | * @return
420 | */
421 | public BleConfig autoConnect(boolean autoConnect) {
422 | this.autoConnect = autoConnect;
423 | return this;
424 | }
425 |
426 | /**
427 | * If set liveUpdateScannedDeviceName = true,Will get real-time Bluetooth device name
428 | * If you do not need real-time updates, please do not set
429 | *
430 | * @param liveUpdateScannedDeviceName
431 | * @return
432 | */
433 | public BleConfig liveUpdateScannedDeviceName(boolean liveUpdateScannedDeviceName) {
434 | this.liveUpdateScannedDeviceName = liveUpdateScannedDeviceName;
435 | return this;
436 | }
437 |
438 | /**
439 | * default value = 200ms
440 | *
441 | * @param millisecond
442 | * @return
443 | */
444 | public BleConfig notifyDelay(int millisecond) {
445 | this.notifyDelay = millisecond;
446 | return this;
447 | }
448 |
449 | /**
450 | * @param splitPacketToSendWhenCmdLenBeyond default value = false
451 | * @param sendTimeInterval unit is ms,default value = 20ms
452 | * The time interval of subcontracting sending shall not be less than 20ms to avoid sending failure
453 | * The default length of each subcontract is 20 bytes
454 | * @return
455 | */
456 | public BleConfig splitPacketToSendWhenCmdLenBeyond(boolean splitPacketToSendWhenCmdLenBeyond, int sendTimeInterval) {
457 | this.splitPacketToSendWhenCmdLenBeyond = splitPacketToSendWhenCmdLenBeyond;
458 | this.sendTimeInterval = sendTimeInterval;
459 | return this;
460 | }
461 |
462 | public BleConfig splitPacketToSendWhenCmdLenBeyond(boolean splitPacketToSendWhenCmdLenBeyond, int sendTimeInterval, int eachSplitPacketLen) {
463 | this.splitPacketToSendWhenCmdLenBeyond = splitPacketToSendWhenCmdLenBeyond;
464 | this.sendTimeInterval = sendTimeInterval;
465 | this.eachSplitPacketLen = eachSplitPacketLen;
466 | return this;
467 | }
468 |
469 | /**
470 | * @param splitPacketToSendWhenCmdLenBeyond default value = false
471 | * sendTimeInterval's unit is ms,default value = 20ms
472 | * The default length of each subcontract is 20 bytes
473 | * @return
474 | */
475 | public BleConfig splitPacketToSendWhenCmdLenBeyond(boolean splitPacketToSendWhenCmdLenBeyond) {
476 | this.splitPacketToSendWhenCmdLenBeyond = splitPacketToSendWhenCmdLenBeyond;
477 | return this;
478 | }
479 |
480 | /**
481 | * set Mtu
482 | *
483 | * @param mtuSize
484 | * @param callback
485 | */
486 | public BleConfig setMtu(int mtuSize, BleMtuChangedCallback callback) {
487 | if (callback == null) {
488 | throw new IllegalArgumentException("BleMtuChangedCallback can not be null !");
489 | }
490 |
491 | if (mtuSize > ValueLimit.DEFAULT_MAX_MTU) {
492 | callback.onSetMTUFailure(mtuSize, new BluetoothException("Required mtuSize should lower than 512 !"));
493 | }
494 |
495 | if (mtuSize < ValueLimit.DEFAULT_MTU) {
496 | callback.onSetMTUFailure(mtuSize, new BluetoothException("Required mtuSize should higher than 23 !"));
497 | }
498 |
499 | this.mtuSize = mtuSize;
500 | mBleMtuChangedCallback = callback;
501 | return this;
502 | }
503 |
504 | public boolean isSplitPacketToSendWhenCmdLenBeyond() {
505 | return splitPacketToSendWhenCmdLenBeyond;
506 | }
507 |
508 | public int getSendTimeInterval() {
509 | return sendTimeInterval;
510 | }
511 |
512 | public int getEachSplitPacketLen() {
513 | return eachSplitPacketLen;
514 | }
515 |
516 | public String getServiceUUID() {
517 | return serviceUUID;
518 | }
519 |
520 | public String getWriteCharacteristicUUID() {
521 | return writeCharacteristicUUID;
522 | }
523 |
524 | public String getNotifyCharacteristicUUID() {
525 | return notifyCharacteristicUUID;
526 | }
527 |
528 | public boolean isUseCharacteristicDescriptor() {
529 | return useCharacteristicDescriptor;
530 | }
531 |
532 | public boolean isLiveUpdateScannedDeviceName() {
533 | return liveUpdateScannedDeviceName;
534 | }
535 |
536 | public boolean isAutoConnect() {
537 | return autoConnect;
538 | }
539 |
540 | public int getMtuSize() {
541 | return mtuSize;
542 | }
543 |
544 | public int getNotifyDelay() {
545 | return notifyDelay;
546 | }
547 |
548 | public BleMtuChangedCallback getBleMtuChangedCallback() {
549 | return mBleMtuChangedCallback;
550 | }
551 | }
552 |
553 |
554 | /**
555 | * Call this method when you need to disconnect
556 | */
557 | public synchronized void release() {
558 | isUserActiveDisconnect = true;
559 | cancelScan();
560 | destroyChannel();
561 | resetCallBack();
562 | }
563 |
564 |
565 | private static Context APPLICATION_CONTEXT;
566 |
567 | /**
568 | * 初始化context,如果由于不同机型导致反射获取context失败可以在Application调用此方法
569 | */
570 | public static void init(Context context) {
571 | APPLICATION_CONTEXT = context;
572 | }
573 |
574 | /**
575 | * 反射获取 application context
576 | */
577 | public static Context getContext() {
578 | if (null == APPLICATION_CONTEXT) {
579 | try {
580 | Application application = (Application) Class.forName("android.app.ActivityThread")
581 | .getMethod("currentApplication")
582 | .invoke(null, (Object[]) null);
583 | if (application != null) {
584 | APPLICATION_CONTEXT = application;
585 | return application;
586 | }
587 | } catch (Exception e) {
588 | e.printStackTrace();
589 | }
590 |
591 | try {
592 | Application application = (Application) Class.forName("android.app.AppGlobals")
593 | .getMethod("getInitialApplication")
594 | .invoke(null, (Object[]) null);
595 | if (application != null) {
596 | APPLICATION_CONTEXT = application;
597 | return application;
598 | }
599 | } catch (Exception e) {
600 | e.printStackTrace();
601 | }
602 |
603 | throw new IllegalStateException("ContextHolder is not initialed, it is recommend to init with application context.");
604 | }
605 | return APPLICATION_CONTEXT;
606 | }
607 |
608 |
609 | }
610 |
--------------------------------------------------------------------------------
/BluetoothLib/src/main/java/com/hjy/bluetooth/operator/impl/BluetoothConnector.java:
--------------------------------------------------------------------------------
1 | package com.hjy.bluetooth.operator.impl;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothGatt;
7 | import android.bluetooth.BluetoothGattCallback;
8 | import android.bluetooth.BluetoothGattCharacteristic;
9 | import android.bluetooth.BluetoothGattService;
10 | import android.bluetooth.BluetoothProfile;
11 | import android.content.BroadcastReceiver;
12 | import android.content.Context;
13 | import android.content.Intent;
14 | import android.content.IntentFilter;
15 | import android.os.AsyncTask;
16 | import android.os.Build;
17 | import android.os.Handler;
18 | import android.os.Looper;
19 | import android.text.TextUtils;
20 |
21 | import com.hjy.bluetooth.HBluetooth;
22 | import com.hjy.bluetooth.async.ClassicBluetoothConnectTask;
23 | import com.hjy.bluetooth.constant.BluetoothState;
24 | import com.hjy.bluetooth.constant.ClassicBluetoothPairMode;
25 | import com.hjy.bluetooth.exception.BluetoothException;
26 | import com.hjy.bluetooth.inter.BleMtuChangedCallback;
27 | import com.hjy.bluetooth.inter.BleNotifyCallBack;
28 | import com.hjy.bluetooth.inter.ClassicBluetoothPairCallBack;
29 | import com.hjy.bluetooth.inter.ConnectCallBack;
30 | import com.hjy.bluetooth.operator.abstra.Connector;
31 | import com.hjy.bluetooth.operator.abstra.Sender;
32 | import com.hjy.bluetooth.utils.BleNotifier;
33 | import com.hjy.bluetooth.utils.ReceiveHolder;
34 |
35 | import java.lang.reflect.InvocationTargetException;
36 | import java.lang.reflect.Method;
37 | import java.util.HashMap;
38 | import java.util.List;
39 | import java.util.Map;
40 | import java.util.UUID;
41 |
42 | import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
43 |
44 | /**
45 | * Created by _H_JY on 2018/10/20.
46 | */
47 |
48 | public class BluetoothConnector extends Connector {
49 |
50 | private Context mContext;
51 | private BluetoothAdapter bluetoothAdapter;
52 | private ClassicBluetoothConnectTask connectAsyncTask;
53 | private ConnectCallBack connectCallBack;
54 | private BleNotifyCallBack bleNotifyCallBack;
55 | private ClassicBluetoothPairCallBack pairCallBack;
56 | private Handler handler;
57 | private Map