├── bluetoothlib
├── class_files.txt
├── mapping.txt
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── com
│ │ │ └── clock
│ │ │ └── bluetoothlib
│ │ │ ├── Test1.java
│ │ │ └── logic
│ │ │ ├── network
│ │ │ ├── connection
│ │ │ │ ├── BLEScanDevice.java
│ │ │ │ ├── DeviceState.java
│ │ │ │ ├── BLEAppDevice.java
│ │ │ │ ├── BLEState.java
│ │ │ │ ├── BLEServerListener.java
│ │ │ │ ├── BLEConnector.java
│ │ │ │ ├── BLEBaseDevice.java
│ │ │ │ ├── BLEDeviceBiz.java
│ │ │ │ ├── BLEScanner.java
│ │ │ │ ├── BLEServerCentral.java
│ │ │ │ └── BLEBaseManager.java
│ │ │ ├── DeviceInfoSyncListener.java
│ │ │ ├── DeviceAuthorizeListener.java
│ │ │ └── data
│ │ │ │ ├── BLEPacket.java
│ │ │ │ ├── DataParserAdapter.java
│ │ │ │ ├── BLEAllDispatcher.java
│ │ │ │ └── DataCircularBuffer.java
│ │ │ ├── analysis
│ │ │ ├── OnDataSendCallBack.java
│ │ │ ├── OnRespDataListener.java
│ │ │ ├── OnRespResultListener.java
│ │ │ ├── NotifyCmdAdapter.java
│ │ │ ├── ConstantTransfer.java
│ │ │ ├── DecodeAdapter.java
│ │ │ ├── CmdInterface.java
│ │ │ ├── DataTransferManager.java
│ │ │ ├── NotifyCmdLib.java
│ │ │ ├── CmdBase.java
│ │ │ ├── Decode.java
│ │ │ └── RecvCallbackManager.java
│ │ │ └── utils
│ │ │ ├── ThreadPoolUtil.java
│ │ │ └── LogUtil.java
│ │ └── AndroidManifest.xml
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── app
├── libs
│ └── eventbus-3.1.1.jar
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable-xhdpi
│ │ │ └── loading.png
│ │ ├── drawable-xxhdpi
│ │ │ └── bledevice.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── drawable
│ │ │ ├── anim_rotate_load.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ │ ├── popwindow_scan_list.xml
│ │ │ ├── dialog_loading2.xml
│ │ │ ├── item_list_scan.xml
│ │ │ ├── activity_main.xml
│ │ │ └── item_device_list_device.xml
│ │ └── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ └── com
│ │ │ └── clock
│ │ │ └── blelib
│ │ │ ├── adapter
│ │ │ ├── OnItemClickListener.java
│ │ │ ├── ScanListAdapter.java
│ │ │ └── DeviceListAdapter.java
│ │ │ ├── event
│ │ │ ├── BleSendResultEvent.java
│ │ │ ├── AddPreConnDeviceEvent.java
│ │ │ ├── updateDeviceInfoEvent.java
│ │ │ ├── AddNewDeviceEvent.java
│ │ │ ├── ReconnectDeviceEvent.java
│ │ │ ├── ConnectDeviceEvent.java
│ │ │ └── AddScanDeviceEvent.java
│ │ │ ├── bleissus
│ │ │ ├── blemodel
│ │ │ │ ├── heart
│ │ │ │ │ ├── HeaterDataAdapter.java
│ │ │ │ │ └── HeaterDevice.java
│ │ │ │ ├── bracelet
│ │ │ │ │ └── BraceletDevice.java
│ │ │ │ ├── led
│ │ │ │ │ ├── LedDataAdapter.java
│ │ │ │ │ ├── BLELedNecessaryBiz.java
│ │ │ │ │ └── LedDevice.java
│ │ │ │ └── BLEManager.java
│ │ │ └── ProtocolVh.java
│ │ │ ├── util
│ │ │ └── WidgetUitl.java
│ │ │ ├── widget
│ │ │ └── ScanListPopWindow.java
│ │ │ ├── BaseActivity.java
│ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── local.properties
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/bluetoothlib/class_files.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bluetoothlib/mapping.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':bluetoothlib'
2 |
--------------------------------------------------------------------------------
/app/libs/eventbus-3.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruigeyun/Android-DualBle/HEAD/app/libs/eventbus-3.1.1.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruigeyun/Android-DualBle/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/bluetoothlib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BluetoothLib
3 |
4 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/Test1.java:
--------------------------------------------------------------------------------
1 | package com.clock.bluetoothlib;
2 |
3 | public class Test1 {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruigeyun/Android-DualBle/HEAD/app/src/main/res/drawable-xhdpi/loading.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/bledevice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruigeyun/Android-DualBle/HEAD/app/src/main/res/drawable-xxhdpi/bledevice.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruigeyun/Android-DualBle/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DualBle
3 |
4 | Please turn on bluetooth first
5 | Device connect max number 7!
6 |
7 |
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jul 29 15:48:13 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/anim_rotate_load.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEScanDevice.java:
--------------------------------------------------------------------------------
1 | package com.clock.bluetoothlib.logic.network.connection;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 |
5 | public class BLEScanDevice {
6 | BluetoothDevice scanBle;
7 | int deviceType;
8 |
9 | BLEScanDevice(BluetoothDevice scanBle, int deviceType) {
10 | this.scanBle = scanBle;
11 | this.deviceType = deviceType;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file is automatically generated by Android Studio.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file should *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 | #
7 | # Location of the SDK. This is only used by Gradle.
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | sdk.dir=D\:\\software\\android\\sdk
11 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/OnDataSendCallBack.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | public interface OnDataSendCallBack {
9 |
10 | public void onSendData(byte[] values, int deviceId);
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/OnRespDataListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | /**
9 | * 数据发送后,回应的数据
10 | * @param
11 | */
12 | public interface OnRespDataListener {
13 | void onResponse(T resultSrc);
14 | }
15 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/OnRespResultListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | /**
9 | * 数据请求后,回应的最终结果
10 | */
11 | public interface OnRespResultListener {
12 | void onSuccess(int code);
13 | void onFailure(int code);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/adapter/OnItemClickListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.adapter;
7 |
8 |
9 | /**
10 | * Created by ysh_leo on 2018/3/5.
11 | */
12 |
13 | public interface OnItemClickListener {
14 | void onItemClick(int position);
15 | void onItemLongClick(int position);
16 | }
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/NotifyCmdAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | import java.util.HashMap;
9 |
10 | public abstract class NotifyCmdAdapter {
11 |
12 | public abstract void getNotifyCmds(HashMap cmdLib);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/BleSendResultEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 | /**
9 | * 发送结果
10 | */
11 | public class BleSendResultEvent {
12 |
13 | public String result;
14 |
15 | public BleSendResultEvent(String result) {
16 | this.result = result;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/DeviceInfoSyncListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network;
7 |
8 | public interface DeviceInfoSyncListener {
9 |
10 | /**
11 | * 同步结束后,调用此接口,设备进入正式使用状态
12 | * @param type 目前没有意义
13 | */
14 | public void onInfoSync(int type);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/DeviceAuthorizeListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network;
7 |
8 | public interface DeviceAuthorizeListener {
9 | /**
10 | * 校验通过,继续往下走,校验失败,删除设备
11 | * @param isAuthorize true 校验通过, false 校验失败
12 | */
13 | public void onAuthorize(boolean isAuthorize);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/AddPreConnDeviceEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
10 |
11 | public class AddPreConnDeviceEvent {
12 | public BLEAppDevice device;
13 |
14 | public AddPreConnDeviceEvent(BLEAppDevice device) {
15 | this.device = device;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/updateDeviceInfoEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
10 |
11 | public class updateDeviceInfoEvent {
12 |
13 | public BLEAppDevice device;
14 |
15 | public updateDeviceInfoEvent(BLEAppDevice device) {
16 | this.device = device;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/AddNewDeviceEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
10 |
11 | /**
12 | * 连接上新设备,到设备列表中显示
13 | */
14 | public class AddNewDeviceEvent {
15 |
16 | public BLEAppDevice device;
17 |
18 | public AddNewDeviceEvent(BLEAppDevice device) {
19 | this.device = device;
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/ConstantTransfer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | public class ConstantTransfer {
9 |
10 | public static final int CODE_SUCCESS = 0;
11 | public static final int CODE_FAILURE = -1;
12 | public static final int CODE_EXCEPTION = -2;
13 | public static final int CODE_TIMEOUT = -3;
14 | public static final int CODE_DATA_NULL = -4;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/DeviceState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | public enum DeviceState {
9 |
10 | Normal(0), // normal 状态的设备,才可以被连接
11 | Remove(1), // 被删除
12 | DisConnRelease(2);// 被断开,在释放资源状态,此状态下,不可以被连接
13 |
14 | // 成员变量
15 | private int index;
16 | // 构造方法
17 | private DeviceState(int index) {
18 | this.index = index;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/ReconnectDeviceEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
10 |
11 | /**
12 | * 重连设备,分:自动连接,手动连接
13 | * 手动连接:指在设备列表的设备,被新设备顶下去了,再手动连接
14 | * 自动连接:设备蓝牙自爆异常,自动去连接,恢复状态
15 | */
16 | public class ReconnectDeviceEvent {
17 |
18 | public BLEAppDevice device;
19 |
20 | public ReconnectDeviceEvent(BLEAppDevice device) {
21 | this.device = device;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/ConnectDeviceEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
10 |
11 | /**
12 | * 连接扫描列表的设备,有些设备无法识别,则删除
13 | */
14 | public class ConnectDeviceEvent {
15 |
16 | public BLEAppDevice device;
17 | public int type; // 连接反馈,无法识别的设备,删除
18 |
19 | public ConnectDeviceEvent(BLEAppDevice device, int type) {
20 | this.device = device;
21 | this.type = type;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/DecodeAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | public abstract class DecodeAdapter {
9 | public DecodeAdapter() {
10 | }
11 | /**
12 | * 命令字节的位置
13 | * @return
14 | */
15 | public abstract byte getIndexCmd();
16 | /**
17 | * 校验码的位置
18 | * @return
19 | */
20 | public abstract int getIndexCheckCode();
21 | /**
22 | * 包标识 到 内容 间的距离,找到内容长的位置
23 | * @return
24 | */
25 | public abstract int getDistMarkToContent();
26 | }
27 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/CmdInterface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | /**
9 | * 命令模式
10 | * @author clock
11 | *
12 | */
13 | public interface CmdInterface {
14 | /**
15 | *
16 | * @param data 一包数据
17 | * @param index 命令的内容部分开始的位置
18 | * @param len 命令内容部分的长度
19 | * @return
20 | */
21 | public int parseNotifyCmd(byte[] data, int index, int len, int deviceId);
22 | public int parseTransmitCmd(byte[] data, int index, int len);
23 | public int parseConfigCmd(byte[] data, int index, int len);
24 | public boolean packDataForSend();
25 | }
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/popwindow_scan_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_loading2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/event/AddScanDeviceEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.event;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 |
10 | public class AddScanDeviceEvent {
11 | private BluetoothDevice bluetoothDevice;
12 |
13 | public AddScanDeviceEvent(BluetoothDevice bluetoothDevice) {
14 | this.bluetoothDevice = bluetoothDevice;
15 | }
16 |
17 | public BluetoothDevice getBluetoothDevice() {
18 | return bluetoothDevice;
19 | }
20 |
21 | public void setBluetoothDevice(BluetoothDevice bluetoothDevice) {
22 | this.bluetoothDevice = bluetoothDevice;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | final StringBuffer sb = new StringBuffer("ScanEvent{");
28 | sb.append("bluetoothDevice=").append(bluetoothDevice);
29 | sb.append('}');
30 | return sb.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.clock.blelib"
7 | minSdkVersion 19
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 |
29 | implementation 'com.android.support:recyclerview-v7:28.0.0'
30 | implementation files('libs/eventbus-3.1.1.jar')
31 | implementation project(path: ':bluetoothlib')
32 | }
33 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEAppDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 |
10 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
11 |
12 | public abstract class BLEAppDevice extends BLELogicDevice {
13 | /**
14 | * 没有被扫描出来,但是之前连接过,需要显示在列表,这样的设备定义为虚拟设备,在界面手动连接虚拟设备,需启动扫描,扫描到再连接,非虚拟设备,不需要再启动扫描。
15 | * 虚拟设备连接成功后,将在列表中替换,所以这边变量没有清空处理
16 | */
17 | public boolean isVirtualDevice = false;
18 |
19 | public BLEAppDevice() {}
20 |
21 | public BLEAppDevice(BluetoothDevice device, DataParserAdapter adapter){
22 | super(adapter);
23 | }
24 |
25 | // @Override
26 | // public UUID getServiceUUID() {
27 | // return null;
28 | // }
29 | //
30 | // @Override
31 | // public UUID getRxUUID() {
32 | // return null;
33 | // }
34 | //
35 | // @Override
36 | // public UUID getTxUUID() {
37 | // return null;
38 | // }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/data/BLEPacket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.data;
7 |
8 | public final class BLEPacket {
9 |
10 | /**
11 | * 数据
12 | */
13 | public byte[] bleData;
14 | /**
15 | * ble id 标志不同的蓝牙设备,也就是设备的mDeviceId
16 | */
17 | public int bleDeviceId;
18 |
19 | public BLEPacket(byte[] data, int id) {
20 | bleData = data;
21 | bleDeviceId = id;
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | final StringBuffer sb = new StringBuffer("BLEPacket{");
27 | sb.append("bleData=");
28 | if (bleData == null) sb.append("null");
29 | else {
30 | sb.append('[');
31 | for (int i = 0; i < bleData.length; ++i)
32 | sb.append(i == 0 ? "" : ", ").append(bleData[i]);
33 | sb.append(']');
34 | }
35 | sb.append(", bleAddr='").append(bleDeviceId).append('\'');
36 | sb.append('}');
37 | return sb.toString();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/heart/HeaterDataAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel.heart;
7 |
8 | import com.clock.blelib.bleissus.ProtocolVh;
9 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
10 |
11 | public class HeaterDataAdapter extends DataParserAdapter {
12 | // aa 0a 07 00 14 00 00 00 00 00
13 | // 包标志 指令 内容长度 内容
14 | public HeaterDataAdapter() {}
15 | /**
16 | * 包标志
17 | * @return
18 | */
19 | @Override
20 | public byte getPackageMark() {
21 | // aa
22 | return (byte) ProtocolVh.SYNC_FLAG_HEADER;
23 | }
24 | /**
25 | * 包数据中,除了包标识,包内容,的其他数据的总长度
26 | * @return
27 | */
28 | @Override
29 | public int getExtraDataNum() {
30 | // 命令字(1字节) + 长度(1字节)0a 07
31 | return ProtocolVh.LED_EXTRA_DATA_CNT;
32 | }
33 | /**
34 | * 包标识 到 内容 间的距离,找到内容长的位置
35 | * @return
36 | */
37 | @Override
38 | public int getDistMarkToContent() {
39 | // aa 0a 07 距离为2
40 | return ProtocolVh.DIST_FLAG_2_LEN;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/utils/ThreadPoolUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.utils;
7 |
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 |
11 | public final class ThreadPoolUtil {
12 |
13 | private ExecutorService mExecutorService;
14 | private static ThreadPoolUtil instance = new ThreadPoolUtil();
15 |
16 | private ThreadPoolUtil() {
17 | // 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
18 | mExecutorService = Executors.newCachedThreadPool();
19 | // // 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
20 | // ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
21 | // // 创建一个定长线程池,支持定时及周期性任务执行
22 | // ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
23 | // // 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
24 | // ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
25 | }
26 |
27 | public static ThreadPoolUtil get() {
28 | return instance;
29 | }
30 |
31 | public ExecutorService getThread() {
32 | return mExecutorService;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_list_scan.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
29 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
24 |
25 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/bracelet/BraceletDevice.java:
--------------------------------------------------------------------------------
1 | package com.clock.blelib.bleissus.blemodel.bracelet;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 |
5 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
6 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
7 |
8 | import java.util.UUID;
9 |
10 | public class BraceletDevice extends BLEAppDevice {
11 | // 蓝牙手环
12 | private final String TAG = "BraceletDevice";
13 | /**
14 | * 此DEVICE_TYPE_ID 不能和别的设备定义的值一样
15 | */
16 | public static final Integer DEVICE_TYPE_ID = 1000;
17 | public static final UUID SERVICE_UUID = UUID.fromString("0000fea0-0000-1000-8000-00805f9b34fb");
18 | private final UUID RX_CHAR_UUID = UUID.fromString("0000fea1-0000-1000-8000-00805f9b34fb");
19 | private final UUID TX_CHAR_UUID = UUID.fromString("0000fea2-0000-1000-8000-00805f9b34fb");
20 |
21 | public int powerState = 1;
22 | public String nickname = "";
23 |
24 | @Override
25 | public UUID getServiceUUID() {
26 | return SERVICE_UUID;
27 | }
28 | @Override
29 | public UUID getRxUUID() {
30 | return RX_CHAR_UUID;
31 | }
32 | @Override
33 | public UUID getTxUUID() {
34 | return TX_CHAR_UUID;
35 | }
36 |
37 | public BraceletDevice(BluetoothDevice device, DataParserAdapter adapter) {
38 |
39 | super(device, adapter);
40 |
41 | }
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-DualBle
2 | 库的使用方法,demo里面,有具体的栗子,仔细阅读下,很多注释的,应该容易理解
3 |
4 | 1、建立自己的蓝牙设备对象,demo中有两种蓝牙设备,蓝牙控制led的设备(LedDevice)、蓝牙控制加热器设的备(HeaterDevice),他们继承蓝牙库的对外设备(BLEAppDevice),添加自己的新特征,如led灯颜色,heater定时时间。蓝牙对象必须包含自己的服务、发送、接收三种uuid,以及自定义一个设备类型id,重写三个抽象方法,把uuid写进去。构造方法必须如下的方式,固定两个参数,并且调用父类的构造方法。
5 |
6 | 2、建立自己蓝牙设备的数据包结构对象(可选),继承DataParserAdapter,重写相应方法。框架内部根据你定义的结构,自动帮你把蓝牙回应的数据包提炼出来(主要是处理断包、粘包问题),最终的数据包通过onDeviceRespSpliceData(BLEPacket message)方法回调给你。当然你也可以不用架构的处理算法,自己拼包,在DataCircularBuffer 类中,pushOriginalDataToBuffer(byte[] originalData)方法,是各个蓝牙设备数据推过来的入口,在这里接入自己的算法。
7 | 如果不建立DataParserAdapter对象,则默认为null,蓝牙回应的数据,通过onDevicesRespOriginalData(BLEPacket message) 方法回调给你。
8 |
9 | 3、建立自己的蓝牙管理对象,继承BLEBaseManager,重写必要的、可选的方法。蓝牙的各种信息交换,都是通过这个类回调给你。很重要!仔细阅读BLEServerListener接口里的方法说明,重写自己需要的方法。 (1)必须重写 onGetDevicesServiceUUID()方法,把自己定义的设备类型ID和设备的service uuid,用map写进去。框架连接上设备后,读取设备的service uuid,根据这个map分辨出是那种类型的设备。
10 | (2)必须重写BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceType)方法,框架识别设备类型后,回调给你,你根据设备类型,创建设备对象实例。
11 | (3)onAddScanDevice(BluetoothDevice bluetoothDevice)方法,框架扫描到设备,就会回调这个方法。
12 | (4)onAddNewDevice(BLEAppDevice device)方法,框架连接成功一个设备,各种状态完备后,回调这个方法。
13 | 这些方法在BLEServerListener接口都有详细说明
14 | 建立三个对象,就可以使用此框架了,如此简单!
15 |
16 | 4、初始化蓝牙框架,APP获得蓝牙相应权限后,调用BLEBaseManager的 initBle(..)方法初始化蓝牙。见demo
17 | 注意
18 |
19 | 多设备同时工作,必定引起并发竞争问题,自己要做好同步!demo只是使用方法,没有处理那些问题。
20 |
21 | 源码中package com.gbd.sourcing.app.sprayer.bleissus.analysis包里的内容,跟蓝牙库没有直接关系,不影响你的使用,直接忽略他们,他们是对蓝牙数据收发的封装,后面的文章会讲到。
22 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/data/DataParserAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.data;
7 |
8 | /**
9 | * 数据解析适配器
10 | * 数据包结构必须是:包标识(1字节)+ 其他(n字节)+ 内容长度(1字节)+内容(n字节)+其他(n字节)
11 | * // aa 0a 07 00 14 00 00 00 00 00
12 | * // 包标志 指令 内容长度 内容
13 | */
14 | public abstract class DataParserAdapter {
15 | public DataParserAdapter() {
16 | }
17 | /**
18 | * 包标志
19 | * 例子中:为 aa
20 | * @return
21 | */
22 | public abstract byte getPackageMark();
23 | /**
24 | * 包数据中,除了包标识、包内容,其他数据的总个数(命令字、校验值、预留数据等)
25 | * 例子中: 命令字(1字节) + 长度(1字节)0a 07 为2
26 | * @return
27 | */
28 | public abstract int getExtraDataNum();
29 | /**
30 | * 包标识 到 内容 间的距离,找到内容长的位置
31 | * 例子中:aa 0a 07 距离为2
32 | * @return
33 | */
34 | public abstract int getDistMarkToContent();
35 | /**
36 | * 获取 包头 到 包尾 的长度,不包括包头
37 | * contentLen 根据内容长度,获得包头 到 包尾 的长度
38 | * 数据包符合要求的,不需要重写这个方法
39 | * @return
40 | */
41 | public int getLenPackageHeadToTail(int contentLen) {
42 | return contentLen + getExtraDataNum();
43 | }
44 | /**
45 | * 获取一整包数据的长度
46 | * contentLen 根据内容长度,获取一整包数据的长度
47 | * 数据包符合要求的,不需要重写这个方法
48 | * @return
49 | */
50 | public int getLenOnePackage(int contentLen) {
51 | return contentLen + getExtraDataNum() + 1;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/led/LedDataAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel.led;
7 |
8 |
9 | import com.clock.blelib.bleissus.ProtocolVh;
10 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
11 |
12 | public class LedDataAdapter extends DataParserAdapter {
13 |
14 | public LedDataAdapter() {}
15 | /**
16 | * 包标志
17 | * @return
18 | */
19 | @Override
20 | public byte getPackageMark() {
21 | return (byte) ProtocolVh.SYNC_FLAG_HEADER;
22 | }
23 | /**
24 | * 包数据中,除了包标识,包内容,的其他数据的总长度
25 | * @return
26 | */
27 | @Override
28 | public int getExtraDataNum() {
29 | // 命令字(1字节) + 长度(1字节)
30 | return ProtocolVh.LED_EXTRA_DATA_CNT;
31 | }
32 | /**
33 | * 包标识 到 内容 间的距离,找到内容长的位置
34 | * @return
35 | */
36 | @Override
37 | public int getDistMarkToContent() {
38 | return ProtocolVh.DIST_FLAG_2_LEN;
39 | }
40 | /**
41 | * 获取 包头 到 包尾 的长度,不包括包头
42 | * contentLen 根据内容长度,获得包头 到 包尾 的长度
43 | * @return
44 | */
45 | @Override
46 | public int getLenPackageHeadToTail(int contentLen) {
47 | return contentLen - 1;
48 | }
49 | /**
50 | * 获取一整包数据的长度
51 | * contentLen 根据内容长度,获取一整包数据的长度
52 | * @return
53 | */
54 | @Override
55 | public int getLenOnePackage(int contentLen) {
56 | return contentLen;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/DataTransferManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | public abstract class DataTransferManager {
9 |
10 | // private static DataTransferManager instance = new DataTransferManager();
11 | // private DataTransferManager(){}
12 | //
13 | // public static DataTransferManager getInstance() {
14 | // return instance;
15 | // }
16 |
17 | public void initDataTransfer(OnDataSendCallBack callBack, DecodeAdapter adapter, NotifyCmdAdapter adapter2) {
18 | RecvCallbackManager.get().setCallBack(callBack);
19 | Decode.get().setDecodeAdapter(adapter);
20 | NotifyCmdLib.get().setAdapter(adapter2);
21 | }
22 |
23 | public void sendConfigCommand(OnRespDataListener l, byte[] values, int cmdId, int deviceId) {
24 |
25 | RecvCallbackManager.get().sendConfigCommand(l, values, cmdId, deviceId);
26 | }
27 |
28 | public void sendWriteCommand(OnRespDataListener l, byte[] values, int cmdId, int deviceId) {
29 |
30 | RecvCallbackManager.get().sendWriteCommand(l, values, cmdId, deviceId);
31 | }
32 |
33 | public void DecodeRespData(byte[] data, int deviceId) {
34 | RecvCallbackManager.get().parseRecvPackageData(data, deviceId);
35 | }
36 |
37 | public int parseTransmitData(byte[] data, CmdBase cmd) {
38 |
39 | return Decode.get().parseTransmitData(data, cmd);
40 | }
41 |
42 | public int parseConfigData(byte[] data, CmdBase cmd) {
43 | return Decode.get().parseConfigData(data, cmd);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/heart/HeaterDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel.heart;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 |
10 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
11 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
12 |
13 | import java.util.UUID;
14 |
15 | public class HeaterDevice extends BLEAppDevice {
16 | private final String TAG = "BLELedDevice";
17 | /**
18 | * 此DEVICE_TYPE_ID 不能和别的设备定义的值一样
19 | */
20 | public static final Integer DEVICE_TYPE_ID = 1001;
21 | public static final UUID SERVICE_UUID = UUID.fromString("0000FFF0-0000-1000-8000-00805f9b34fb");
22 | private final UUID RX_CHAR_UUID = UUID.fromString("0000FFF1-0000-1000-8000-00805f9b34fb");
23 | private final UUID TX_CHAR_UUID = UUID.fromString("0000FFF2-0000-1000-8000-00805f9b34fb");
24 |
25 | // public static final UUID UUID_SETTING = UUID.fromString("0000ffa0-0000-1000-8000-00805f9b34fb");//配置模块参数
26 |
27 | public int powerState = 1;
28 | public String name = "";
29 | public String image = "";
30 |
31 | public int timer = 3;
32 | public int powerLevel = 1;
33 |
34 | @Override
35 | public UUID getServiceUUID() {
36 | return SERVICE_UUID;
37 | }
38 | @Override
39 | public UUID getRxUUID() {
40 | return RX_CHAR_UUID;
41 | }
42 | @Override
43 | public UUID getTxUUID() {
44 | return TX_CHAR_UUID;
45 | }
46 |
47 | public HeaterDevice(BluetoothDevice device, DataParserAdapter adapter) {
48 |
49 | super(device, adapter);
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | public final class BLEState {
9 |
10 | public enum Scan {
11 | ScanIdle(0), // 系统还没准备好,初始状态
12 | CanScan(1), // 可以开始扫描
13 | Scanning(2), // 正在扫描
14 | Scanned(3); // 扫描结束了
15 |
16 | private int index;
17 | private Scan(int index) {
18 | this.index = index;
19 | }
20 | }
21 |
22 | public enum Connect {
23 | ConnectIdle(0), //
24 | ConnectFail(2),
25 | Disconnect(3), //
26 | BeToDisconnect(4), // 当前设备被其他设备挤下去,准备进入断开状态
27 | ForceDisconnect(5), // 被强制断开连接
28 | InitiativeConnect(6), // 主动去连
29 |
30 | Connecting(10), //
31 | Connected(12), //
32 |
33 | Enable(20), // 特性配置成功,读写方式配置完成,蓝牙设备才真正能通讯
34 | Authorize(50),// 授权通过的设备,就可以交互业务数据了
35 | Active(100); // 基础数据通讯配置完成,进入正常业务流程
36 |
37 | public int index;
38 | private Connect(int index) {
39 | this.index = index;
40 | }
41 | }
42 |
43 | public enum Reconnect {
44 | ReConnIdle(0),
45 | Reconnecting(1); //
46 |
47 | // 成员变量
48 | private int index;
49 | // 构造方法
50 | private Reconnect(int index) {
51 | this.index = index;
52 | }
53 | }
54 |
55 | public enum ReconnectType {
56 | Auto(0),
57 | Manual(1); //
58 |
59 | // 成员变量
60 | private int index;
61 | // 构造方法
62 | private ReconnectType(int index) {
63 | this.index = index;
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/bluetoothlib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 28
10 | versionCode 107
11 | versionName "1.0.7"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 |
18 |
19 | // buildTypes {
20 | // release {
21 | // minifyEnabled true
22 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | // }
24 | // }
25 |
26 | buildTypes {
27 | release {
28 | /** 不显示LOG **/
29 | /** 混淆 **/
30 | minifyEnabled true
31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
32 | }
33 | debug {
34 | /** 不显示LOG **/
35 | /** 混淆 **/
36 | minifyEnabled true
37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
38 | }
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation fileTree(dir: 'libs', include: ['*.jar'])
44 |
45 | implementation 'com.android.support:appcompat-v7:28.0.0'
46 |
47 | // testImplementation 'junit:junit:4.12'
48 | // androidTestImplementation 'com.android.support.test:runner:1.0.2'
49 | // androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
50 | }
51 |
52 |
53 | def SDK_BASENAME = "blelogiclib";
54 | //修改jar名字+将指定jar生成的地方
55 | task makeClockJar(type:Copy, dependsOn: "build" ){
56 | //如果之前存在,则先删除
57 | delete 'build/libs/blelogiclib.jar'
58 | //设置拷贝的文件
59 | from('build/intermediates/packaged-classes/release/')
60 | //生成jar包后的文件目录位置
61 | into('build/libs/')
62 | //include,exclude参数来设置过滤
63 | include('classes.jar')
64 | //重命名
65 | rename('classes.jar',SDK_BASENAME + '.jar')
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/NotifyCmdLib.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 |
9 | import android.util.Log;
10 |
11 | import java.util.HashMap;
12 |
13 | class NotifyCmdLib {
14 | private final String TAG = NotifyCmdLib.class.getSimpleName();
15 | private HashMap cmdLib = new HashMap();
16 | private HashMap ObjectLib = new HashMap();
17 | // private Map ObjectLib = new ConcurrentHashMap(); // 没有并发情况
18 | private static NotifyCmdLib instance = new NotifyCmdLib();
19 |
20 | NotifyCmdAdapter notifyCmdAdapter;
21 |
22 | private NotifyCmdLib() {
23 | }
24 |
25 | public static NotifyCmdLib get() {
26 | return instance;
27 | }
28 |
29 | public void setAdapter(NotifyCmdAdapter adapter) {
30 | notifyCmdAdapter = adapter;
31 | notifyCmdAdapter.getNotifyCmds(cmdLib);
32 | }
33 |
34 | String getClassString(int key) {
35 | if (cmdLib.containsKey(key)) {
36 | return cmdLib.get(key);
37 | } else {
38 | // Log.d(TAG, "getClassString null");
39 | return null;
40 | }
41 | }
42 |
43 | void putObjectToLib(int key, CmdInterface instance) {
44 | if (ObjectLib.containsKey(key)) {
45 | Log.d(TAG, "对象实例库中,已经存在该对象");
46 | } else {
47 | ObjectLib.put(key, instance);
48 | }
49 | }
50 |
51 | CmdInterface getObjectFromLib(int key) {
52 | if (ObjectLib.containsKey(key)) {
53 | return ObjectLib.get(key);
54 | } else {
55 | Log.d(TAG, "getObjectFromLib null");
56 | return null;
57 | }
58 | }
59 |
60 | public void removeAllParamLib(Integer param) {
61 | cmdLib.clear();
62 | ObjectLib.clear();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/CmdBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | import java.util.Arrays;
9 |
10 |
11 | public class CmdBase implements CmdInterface {
12 | protected String TAG = "CmdInterface";
13 | protected int id = 0;
14 | protected int result = -1;
15 | protected byte[] value = null;
16 |
17 | public CmdBase(){
18 | result = -1;
19 | };
20 |
21 | public int getId() {
22 | return id;
23 | }
24 | public void setId(int id) {
25 | this.id = id;
26 | }
27 | public int getResult() {
28 | return result;
29 | }
30 | public void setResult(int result) {
31 | this.result = result;
32 | }
33 | public byte[] getValue() {
34 | return value;
35 | }
36 | public void setValue(byte[] value) {
37 | this.value = value;
38 | }
39 |
40 | public int getValueLen() {
41 | if(value == null) {
42 | return 0;
43 | }
44 | else {
45 | return value.length;
46 | }
47 | }
48 |
49 | @Override
50 | public int parseNotifyCmd(byte[] data, int index, int len, int deviceId) {
51 | // TODO Auto-generated method stub
52 | if(data == null) {
53 | return ConstantTransfer.CODE_DATA_NULL;
54 | }
55 | // if(len == 0) {
56 | // return ProtocolVh.RESP_LEN_0;
57 | // }
58 |
59 | return ConstantTransfer.CODE_SUCCESS;
60 | }
61 |
62 | @Override
63 | public int parseTransmitCmd(byte[] data, int index, int len) {
64 | // TODO Auto-generated method stub
65 | if(data == null) {
66 | return ConstantTransfer.CODE_DATA_NULL;
67 | }
68 | // byte[]{(byte) 0xaa, (byte)0xee, (byte)0x00};
69 | if(len==0 && data.length==3 && data[1]==(byte)0xee) {
70 | return ConstantTransfer.CODE_TIMEOUT;
71 | }
72 | // if(len == 0) {
73 | // return ProtocolVh.RESP_LEN_0;
74 | // }
75 | return ConstantTransfer.CODE_SUCCESS;
76 | }
77 |
78 | @Override
79 | public int parseConfigCmd(byte[] data, int index, int len) {
80 | // TODO Auto-generated method stub
81 | if(data == null) {
82 | return ConstantTransfer.CODE_DATA_NULL;
83 | }
84 | // if(len == 0) {
85 | // return ProtocolVh.RESP_LEN_0;
86 | // }
87 | return ConstantTransfer.CODE_SUCCESS;
88 | }
89 |
90 | /**
91 | * 如果发送的数据携带参数,必须实现这个方法,把数据打包
92 | */
93 | @Override
94 | public boolean packDataForSend() {
95 | // TODO Auto-generated method stub
96 | return false;
97 | }
98 |
99 | @Override
100 | public String toString() {
101 | return "CmdBase [id=" + id + ", result=" + result + ", value="
102 | + Arrays.toString(value) + "]";
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_device_list_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
22 |
23 |
33 |
34 |
46 |
58 |
70 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/ProtocolVh.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus;
7 |
8 | public class ProtocolVh {
9 |
10 | public static String TAG = "ProtocolVh";
11 |
12 | public static final boolean enableCMD81Test = true; //81指令多出一个帧号,不算校验和内
13 |
14 | /************************** 包的基本框架数据 ********************************/
15 | // 标识位
16 | public static final int SYNC_FLAG_HEADER = 0xAA;
17 | // 命令字
18 | // 内容长度
19 | // 内容数据 n byte
20 | // 校验码
21 | public static final int SYNC_FLAG_END = 0xAA;
22 |
23 | public static final int INDEX_CHECK_CODE = 0x01; // 校验码的位置
24 | public static final int INDEX_CMD = 0x01; // 命令类型的位置
25 | public static final int DIST_FLAG_2_LEN = 0x02; // 包标志到包长度的距离
26 | public static final int EXTRA_DATA_CNT = 0x03; // 包头后面的数据中,除了包内容,还有多少个其他字节(校验(1字节)+ 命令字(1字节) + 长度(1字节))
27 | // public static final int EXTRA_DATA_CNT = 0x04; // 包头后面的数据中,除了包内容,还有多少个其他字节(校验(1字节)+ 命令字(1字节) + 长度(1字节)+ 侦序号(1字节))临时添加
28 |
29 | public static final int INDEX_CONTENT = 1 + DIST_FLAG_2_LEN;
30 |
31 | /************************** 命令类型 ********************************/
32 |
33 | public static final byte COMMAND_LOGIN = 0X00;
34 |
35 | // 命令字(1字节) + 长度(1字节)
36 | public static final int LED_EXTRA_DATA_CNT = 0x02;
37 |
38 | public static final int CMD_PWD = 0x71;
39 |
40 | public static final int CMD_SET_POWER2 = 0x0d;
41 | public static final int CMD_SET_Refill = 0x0b;
42 | public static final int CMD_SET_Spray = 0x08;
43 | public static final int CMD_SET_Timer_POWER = 0x0e;
44 | public static final int CMD_SET_Interval = 0x0f;
45 |
46 | public static final int CMD_Query_POWER = 0x10;
47 | public static final int CMD_Query_Interval = 0x04;
48 | public static final int CMD_Query_Timer_power = 0x06;
49 | public static final int CMD_Query_Voltage = 0x02;
50 | public static final int CMD_Query_Refill = 0x09;
51 |
52 | public static final int CMD_Report_Voltage = 0x03;
53 | public static final int CMD_Report_Interval = 0x05;
54 | public static final int CMD_Report_Timer_power = 0x07;
55 | public static final int CMD_Report_Refill = 0x0a;
56 | public static final int CMD_Report_POWER = 0x0c;
57 |
58 | public static byte[] getCommand(byte comId, String command) {
59 | byte[] commandBytes = command.getBytes();
60 | byte[] commandFinal = new byte[2 + commandBytes.length];
61 | commandFinal[0] = (byte) (2 + commandBytes.length);
62 | commandFinal[1] = comId;
63 | System.arraycopy(commandBytes, 0, commandFinal, 2, commandBytes.length);
64 | // myLog.d("commandFinal: " + HexUtil.encodeHexStr(commandFinal));
65 | return commandFinal;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEServerListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 |
10 | import com.clock.bluetoothlib.logic.network.data.BLEPacket;
11 |
12 | public interface BLEServerListener {
13 |
14 | /**
15 | * 蓝牙扫描器扫到的结果,通知管理器,是否需要自动连接这些设备,用于自动扫描模式,库内使用,不对外开放,使用final禁止重写
16 | */
17 | public void onScannedDevice(BluetoothDevice bluetoothDevice);
18 |
19 | /**
20 | * 扫描结束
21 | */
22 | public void onScanOver();
23 | /**
24 | * 返回扫描到的ble
25 | */
26 | public void onAddScanDevice(BluetoothDevice bluetoothDevice);
27 |
28 | /**
29 | * 连接还没确定类型的设备的回调,目前只有连失败才回调,成功:1,失败:-
30 | */
31 | public void onScanConnVirtualDevice(int type);
32 |
33 | /**
34 | * 每成功连接一个ble,就创建一个ble的类实例,自己定义的蓝牙设备,在这个方法里面创建的
35 | * @param bluetoothDevice
36 | * @param deviceTypeId 自定义的类型id
37 | * @return 创建的ble实例,很重要
38 | */
39 | public BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceTypeId);
40 |
41 | /**
42 | * 扫描到设备后,连接设备,连接成功或失败,目前只有连接失败才调用
43 | * 包括自动重连
44 | * @param device
45 | * @param type 成功:1,失败:0
46 | */
47 | public void onConnectDevice(BLEAppDevice device, int type);
48 |
49 | /**
50 | * 连接成功后,发觉服务后,搜索符合自己的特性,成功:1,失败:0
51 | * @param device
52 | * @param type 成功:1,失败:0
53 | */
54 | public void onCharacterMatch(BluetoothDevice device, int type);
55 | /**
56 | * 准备去连接的设备,先显示出来,再连接。
57 | * 如果连接成功后再显示,就感觉比较慢
58 | * @param device
59 | */
60 | public void onAddPreDevice(BluetoothDevice device);
61 | /**
62 | * ble连接成功,特性配置成功,数据接收方式配置成功,后,返回这个ble实例
63 | * 只回调一次
64 | */
65 | public void onAddNewDevice(BLEAppDevice device);
66 |
67 | /**
68 | * 设备重连接成功后回调
69 | * 重连成功一次,回调一次
70 | * @param device
71 | */
72 | public void onReconnectDevice(BLEAppDevice device);
73 |
74 | /**
75 | * 更新设备信息(进入可操作状态、连接断开)
76 | * @param device
77 | */
78 | public void onUpdateDeviceInfo(BLEAppDevice device);
79 |
80 | /**
81 | * 发送ble数据,发送结果反馈,目前只在发送失败情况调用
82 | * @param result 内容
83 | */
84 | public void onDeviceSendResult(String result);
85 |
86 | /**
87 | * 蓝牙设备回应APP的原始数据
88 | * 分两种:
89 | * 1:APP发送,ble回应数据 2:ble主动上报数据
90 | * @param message
91 | */
92 | public void onDevicesRespOriginalData(BLEPacket message);
93 |
94 | /**
95 | * 蓝牙设备回应APP的数据,经过框架拼包算法处理,解决数据丢包、断包等问题,建议用此算法
96 | * @param message
97 | */
98 | public void onDeviceRespSpliceData(BLEPacket message);
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/util/WidgetUitl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.util;
7 |
8 | import android.app.Activity;
9 | import android.app.AlertDialog;
10 | import android.content.DialogInterface;
11 | import android.widget.EditText;
12 | import android.widget.TextView;
13 |
14 | import com.clock.blelib.R;
15 |
16 |
17 | public class WidgetUitl {
18 |
19 | public static void showLiteEditDialog(Activity activity, CharSequence title, EditText edit, final DialogInterface.OnClickListener listener) {
20 | final AlertDialog.Builder editDialog = new AlertDialog.Builder(activity);
21 | editDialog.setTitle("修改");
22 | editDialog.setIcon(R.mipmap.ic_launcher);
23 |
24 | edit.setSingleLine(true);
25 | //设置dialog布局
26 | editDialog.setView(edit);
27 |
28 | //设置按钮
29 | editDialog.setPositiveButton("确定", listener);
30 |
31 | editDialog.create().show();
32 | }
33 |
34 | public static void showLiteTextDialog(Activity activity, CharSequence title, TextView text, CharSequence textContent,
35 | final DialogInterface.OnClickListener listener) {
36 | final AlertDialog.Builder editDialog = new AlertDialog.Builder(activity);
37 | editDialog.setTitle(title);
38 | editDialog.setIcon(R.mipmap.ic_launcher);
39 |
40 | text.setSingleLine(true);
41 | text.setText(textContent);
42 | text.setPadding(30 ,20, 0, 0);
43 | //设置dialog布局
44 | editDialog.setView(text);
45 |
46 | //设置按钮
47 | editDialog.setPositiveButton("确定", listener);
48 |
49 | editDialog.create().show();
50 | }
51 |
52 | public static void showLiteCommDialog(Activity activity, CharSequence title, CharSequence textContent,
53 | final DialogInterface.OnClickListener listener) {
54 | AlertDialog.Builder builder = new AlertDialog.Builder(activity);
55 | builder.setTitle(title);
56 | builder.setMessage(textContent);
57 | builder.setPositiveButton("确定", listener);
58 | AlertDialog alertDialog = builder.create();
59 | alertDialog.show();
60 | }
61 |
62 | public static void showLiteCommDialog2(Activity activity, CharSequence title, CharSequence textContent,
63 | final DialogInterface.OnClickListener listener) {
64 | AlertDialog.Builder builder = new AlertDialog.Builder(activity);
65 | builder.setTitle(title);
66 | builder.setMessage(textContent);
67 | builder.setPositiveButton("确定", listener);
68 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
69 | @Override
70 | public void onClick(DialogInterface dialog, int which) {
71 | // Log.d(TAG,"点击了取消");
72 | }
73 | });
74 | AlertDialog alertDialog = builder.create();
75 | alertDialog.show();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/led/BLELedNecessaryBiz.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel.led;
7 |
8 | import android.util.Log;
9 |
10 | import com.clock.bluetoothlib.logic.network.DeviceInfoSyncListener;
11 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
12 |
13 | public class BLELedNecessaryBiz {
14 | private final String TAG = "BLEOwnerBiz";
15 |
16 | private BLEAppDevice mOwnerDevice;
17 | private int reSendCnt = 0;
18 | private DeviceInfoSyncListener listener;
19 |
20 | public BLELedNecessaryBiz(BLEAppDevice bleDevice, final DeviceInfoSyncListener listener) {
21 | mOwnerDevice = bleDevice;
22 | this.listener = listener;
23 | }
24 |
25 | public void getDeviceInfo() {
26 | Log.d(TAG, "getDeviceInfo: " + " " + mOwnerDevice.mDeviceId);
27 | // final Cmd73Version cmd = new Cmd73Version();
28 | // DataSendTransferBle.writeBleDevice(cmd, mOwnerDevice.mDeviceId, new OnRespResultListener() {
29 | // @Override
30 | // public void onSuccess(int code) {
31 | // ((LedDevice)mOwnerDevice).hardwareVersion = cmd.getVersion();
32 | // ((LedDevice)mOwnerDevice).dualColor = cmd.getDualColor();
33 | // ((LedDevice)mOwnerDevice).powerState = 0;
34 | //
35 | // mOwnerDevice.updateDeviceInfo(null);
36 | //
37 | // reSendCnt = 0;
38 | // syncPhoneDateToDevice();
39 | // }
40 | //
41 | // @Override
42 | // public void onFailure(int code) {
43 | // if(reSendCnt < 10) {
44 | // getDeviceInfo();
45 | // reSendCnt++;
46 | // }
47 | // }
48 | // });
49 | // 同步结束后,调用
50 | listener.onInfoSync(1); // 数据同步完成后,回调1
51 | }
52 |
53 | private void syncPhoneDateToDevice() {
54 | Log.d(TAG, "syncPhoneDateToDevic: " + " " + mOwnerDevice.mDeviceId);
55 | // final Cmd85SyncPhoneDate cmd = new Cmd85SyncPhoneDate();
56 | // Calendar cal = Calendar.getInstance();
57 | // cmd.setHour(cal.get(Calendar.HOUR_OF_DAY));
58 | // cmd.setMinute(cal.get(Calendar.MINUTE));
59 | // cmd.setSecond(cal.get(Calendar.SECOND));
60 | // cmd.setWeek(cal.get(Calendar.DAY_OF_WEEK)-1);
61 | // if(cmd.getWeek() == 0) {
62 | // cmd.setWeek(7);
63 | // }
64 | // DataSendTransferBle.writeBleDevice(cmd, mOwnerDevice.mDeviceId, new OnRespResultListener() {
65 | // @Override
66 | // public void onSuccess(int code) {
67 | // listener.onInfoSync(1); // 数据同步完成后,回调1
68 | // reSendCnt = 0;
69 | // }
70 | //
71 | // @Override
72 | // public void onFailure(int code) {
73 | // if(reSendCnt < 10) {
74 | // syncPhoneDateToDevice();
75 | // reSendCnt++;
76 | // }
77 | // }
78 | // });
79 | }
80 |
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/data/BLEAllDispatcher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.data;
7 |
8 | import com.clock.bluetoothlib.logic.network.connection.BLEServerListener;
9 | import com.clock.bluetoothlib.logic.network.BleLibByteUtil;
10 |
11 | import java.util.LinkedList;
12 | import java.util.List;
13 |
14 | public final class BLEAllDispatcher extends Thread {
15 |
16 | private final LinkedList packageQueue = new LinkedList<>();
17 | private boolean isStart = false;
18 | public BLEServerListener mBLEServerListener;
19 |
20 | private static BLEAllDispatcher instance = new BLEAllDispatcher();
21 | public static BLEAllDispatcher getInstance() {
22 | return instance;
23 | }
24 | private BLEAllDispatcher() {}
25 |
26 | void addAllPackageData(List data, int deviceId) {
27 | synchronized (packageQueue) {
28 | for (byte[] item: data) {
29 | // d("addAllPackageData : " + BytesUtil.BytesToHexStringPrintf(item));
30 | BLEPacket packet = new BLEPacket(item, deviceId);
31 | packageQueue.add(packet);
32 | }
33 | }
34 | }
35 |
36 | private void removePackageData(BLEPacket data) {
37 | synchronized (packageQueue) {
38 | packageQueue.remove(data);
39 | }
40 | }
41 |
42 | @Override
43 | public void run() {
44 | // TODO Auto-generated method stub
45 | d("PackageSets run..." + Thread.currentThread().getId());
46 | while (!isInterrupted()) {
47 | if (packageQueue.size() > 0) {
48 | byte[] data = packageQueue.get(0).bleData;
49 | d("dispatch : " + BleLibByteUtil.BytesToHexStringPrintf(data));
50 |
51 | dispatchPackage(packageQueue.get(0));
52 | removePackageData(packageQueue.get(0));
53 | // LogUtil.w(TAG,"PackageSets removePackageData: " );
54 | }
55 |
56 | try {
57 | Thread.sleep(20);
58 | } catch (InterruptedException e) {
59 | // TODO Auto-generated catch block
60 | e.printStackTrace();
61 | break;
62 | }
63 | }
64 | d("PackageSets thread over");
65 | }
66 |
67 | synchronized void startRun() {
68 | if (isStart) {
69 | d("disp 已经运行了");
70 | return;
71 | }
72 | isStart = true;
73 | start();
74 | d("启动蓝牙数据分发线程。。。");
75 | }
76 | void stopRun() {
77 | d("stopRun PackageSets Thread");
78 | interrupt();
79 | isStart = false;
80 | packageQueue.clear();
81 | }
82 |
83 | private void d(String msg) {
84 | // Log.w(TAG, msg);
85 | String TAG = "BLEAllDispatcher";
86 | System.out.println(TAG + " " + msg);
87 | }
88 |
89 | private void dispatchPackage(BLEPacket message) {
90 | mBLEServerListener.onDeviceRespSpliceData(message);
91 | }
92 |
93 | public void dispatchOriginalPackage(byte[] data, int id) {
94 | BLEPacket message = new BLEPacket(data, id);
95 | mBLEServerListener.onDevicesRespOriginalData(message);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/Decode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 |
9 | import android.util.Log;
10 |
11 | class Decode {
12 |
13 | private final String TAG = Decode.class.getSimpleName();
14 |
15 | private static Decode instance = new Decode();
16 | private Decode() {};
17 | public static Decode get() {
18 | return instance;
19 | }
20 |
21 | private DecodeAdapter decodeAdapter;
22 | void setDecodeAdapter(DecodeAdapter adapter) {
23 | decodeAdapter = adapter;
24 | }
25 | DecodeAdapter getDecodeAdapter() {
26 | return decodeAdapter;
27 | }
28 |
29 | private boolean calcCheckCode(byte[] data) {
30 | int len = data.length-1;
31 | int result = 0;
32 | for(int index=4; index {
23 |
24 | private final String TAG = "ScanListAdapter";
25 | private List mDatas = new ArrayList();
26 | private OnItemClickListener mOnItemClickListener = null;
27 |
28 | public ScanListAdapter() {
29 | }
30 | public ScanListAdapter(List data) {
31 | mDatas = data;
32 | }
33 |
34 | public void update(List data) {
35 | mDatas = data;
36 | notifyDataSetChanged();
37 | Log.i(TAG, "mDatas: " + mDatas);
38 | }
39 |
40 | public void add(BluetoothDevice data) {
41 | mDatas.add(data);
42 | notifyDataSetChanged();
43 | }
44 |
45 | //在Adapter中实现3个方法
46 | @Override
47 | public int getItemCount() {
48 | return mDatas.size();
49 | }
50 |
51 | @Override
52 | public VH onCreateViewHolder(ViewGroup parent, int viewType) {
53 | View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_scan, parent, false);
54 | return new VH(v);
55 | }
56 |
57 | @Override
58 | public void onBindViewHolder(VH holder, final int position) {
59 | Log.v(TAG, "position: " + position + mDatas.get(position));
60 |
61 | holder.tv_name.setText(mDatas.get(position).getName() + " " + mDatas.get(position).getAddress());
62 |
63 | holder.item_scan_root.setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | Log.w(TAG, "onClick: " + position);
67 | if(mOnItemClickListener != null) {
68 | mOnItemClickListener.onItemClick(position);
69 | }
70 | }
71 | });
72 |
73 | holder.item_scan_root.setOnLongClickListener(new View.OnLongClickListener() {
74 | @Override
75 | public boolean onLongClick(View v) {
76 | Log.w(TAG, "on long Click: " + position);
77 | if(mOnItemClickListener != null) {
78 | // mOnItemClickListener.onItemLongClick(position);
79 | }
80 | return false;
81 | }
82 | });
83 | }
84 |
85 | public void setItemClickListener(OnItemClickListener lis) {
86 | mOnItemClickListener = lis;
87 | }
88 |
89 | public static class VH extends RecyclerView.ViewHolder {
90 | public final TextView tv_name;
91 | public final LinearLayout item_scan_root;
92 |
93 | public VH(View v) {
94 | super(v);
95 | tv_name = v.findViewById(R.id.tv_name);
96 | item_scan_root = v.findViewById(R.id.item_scan_root);
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/utils/LogUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.utils;
7 |
8 | import android.util.Log;
9 |
10 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * 为了方便操作log我们需要自己定义个log类然后在开发阶段将下面LOG_LEVEL 设置为6这样所有的log都能显示,在发布的时候我们将LOG_LEVEL
17 | * 设置为0.这样log就非常方便管理了
18 | *
19 | * @author Administrator
20 | *
21 | */
22 | public final class LogUtil {
23 | public static int LOG_LEVEL = 6;
24 | public static int ERROR = 1;
25 | public static int WARN = 2;
26 | public static int INFO = 3;
27 | public static int DEBUG = 4;
28 | public static int VERBOS = 5;
29 |
30 | public static List tagArray = new ArrayList();
31 |
32 | public static void setLogLevel(int level) {
33 | LOG_LEVEL = level;
34 | // tagArray.add("BitmapManage");
35 | }
36 |
37 | public static void e(String tag, String msg) {
38 | if(!isTagEnable(tag)) {
39 | return;
40 | }
41 | if (LOG_LEVEL > ERROR) {
42 | Log.e(tag, msg);
43 | }
44 | }
45 |
46 | public static void w(String tag, String msg) {
47 | if(!isTagEnable(tag)) {
48 | return;
49 | }
50 | if (LOG_LEVEL > WARN) {
51 | Log.w(tag, msg);
52 | }
53 | }
54 |
55 | public static void i(String tag, String msg) {
56 | if(!isTagEnable(tag)) {
57 | return;
58 | }
59 | if (LOG_LEVEL > INFO) {
60 | Log.i(tag, msg);
61 | }
62 | }
63 |
64 | public static void d(String tag, String msg) {
65 | if(!isTagEnable(tag)) {
66 | return;
67 | }
68 | if (LOG_LEVEL > DEBUG) {
69 | Log.d(tag, msg);
70 | }
71 | }
72 |
73 | public static void v(String tag, String msg) {
74 | if(!isTagEnable(tag)) {
75 | return;
76 | }
77 | if (LOG_LEVEL > VERBOS) {
78 | Log.v(tag, msg);
79 | }
80 | }
81 |
82 | public static void e(String tag, String msg, BLEAppDevice device) {
83 | if(!isTagEnable(tag)) {
84 | return;
85 | }
86 | if (LOG_LEVEL > ERROR) {
87 | Log.e(tag, msg + " " + device.mDeviceId + " " + device.mBleAddress);
88 | }
89 | }
90 |
91 | public static void w(String tag, String msg, BLEAppDevice device) {
92 | if(!isTagEnable(tag)) {
93 | return;
94 | }
95 | if (LOG_LEVEL > WARN) {
96 | Log.w(tag, msg + " " + device.mDeviceId + " " + device.mBleAddress);
97 | }
98 | }
99 |
100 | public static void i(String tag, String msg, BLEAppDevice device) {
101 | if(!isTagEnable(tag)) {
102 | return;
103 | }
104 | if (LOG_LEVEL > INFO) {
105 | Log.i(tag, msg + " " + device.mDeviceId + " " + device.mBleAddress);
106 | }
107 | }
108 |
109 | public static void d(String tag, String msg, BLEAppDevice device) {
110 | if(!isTagEnable(tag)) {
111 | return;
112 | }
113 | if (LOG_LEVEL > DEBUG) {
114 | Log.d(tag, msg + " " + device.mDeviceId + " " + device.mBleAddress);
115 | }
116 | }
117 |
118 | public static void v(String tag, String msg, BLEAppDevice device) {
119 | if(!isTagEnable(tag)) {
120 | return;
121 | }
122 | if (LOG_LEVEL > VERBOS) {
123 | Log.v(tag, msg + " " + device.mDeviceId + " " + device.mBleAddress);
124 | }
125 | }
126 |
127 | private static boolean isTagEnable(String Tag) {
128 | if(tagArray.contains(Tag)) {
129 | return false;
130 | }
131 | return true;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/led/LedDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel.led;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 | import android.util.Log;
10 |
11 | import com.clock.bluetoothlib.logic.network.DeviceAuthorizeListener;
12 | import com.clock.bluetoothlib.logic.network.DeviceInfoSyncListener;
13 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
14 | import com.clock.bluetoothlib.logic.network.data.DataParserAdapter;
15 |
16 | import java.util.UUID;
17 |
18 | public class LedDevice extends BLEAppDevice {
19 | private final String TAG = "BLELedDevice";
20 |
21 | /**
22 | * 此DEVICE_TYPE_ID 不能和别的设备定义的值一样
23 | */
24 | public static final Integer DEVICE_TYPE_ID = 1002;
25 | public static final UUID SERVICE_UUID = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb");
26 | private final UUID RX_CHAR_UUID = UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb");
27 | private final UUID TX_CHAR_UUID = UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb");
28 | /**
29 | * 部分缺陷设备,广播的server uuid 和 实际的server uuid不一致,就多定义一个广播uuid,扫描过滤时,用它,连接成功后,用实际的uuid
30 | */
31 | public static final UUID AD_UUID = UUID.fromString("0000fee7-0000-1000-8000-00805f9b34fb");
32 | @Override
33 | public UUID getServiceUUID() {
34 | return SERVICE_UUID;
35 | }
36 | @Override
37 | public UUID getRxUUID() {
38 | return RX_CHAR_UUID;
39 | }
40 | @Override
41 | public UUID getTxUUID() {
42 | return TX_CHAR_UUID;
43 | }
44 |
45 | public String dualColor = "";
46 | public String hardwareVersion = "";
47 | public int powerState = 0;
48 | public String nickname = "";
49 |
50 | public LedDevice(BluetoothDevice device, DataParserAdapter adapter) {
51 | super(device, adapter);
52 |
53 | }
54 |
55 | @Override
56 | public void onAuthorizeDevice(BLEAppDevice device, DeviceAuthorizeListener listener) {
57 | sendPwdToBleDevice(listener);
58 | }
59 |
60 | @Override
61 | public void onDoNecessaryBiz(BLEAppDevice device, DeviceInfoSyncListener listener) {
62 | new BLELedNecessaryBiz(device, listener).getDeviceInfo();
63 |
64 | }
65 |
66 | private void sendPwdToBleDevice(DeviceAuthorizeListener listener) {
67 | Log.d(TAG, "密码验证: " + mDeviceId);
68 | // Cmd71Pwd cmd = new Cmd71Pwd();
69 | // DataSendTransferBle.writeBleDevice(cmd, mOwnerDevice.mDeviceId, new OnRespResultListener() {
70 | // @Override
71 | // public void onSuccess(int code) {
72 | // Log.i("test", "--led 蓝牙-密码验证成功--=");
73 | // listener.onAuthorize(true); // 验证成功后,回调true
74 | // }
75 | //
76 | // @Override
77 | // public void onFailure(int code) {
78 | // Log.i("test", "--led 蓝牙-密码验证失败--=");
79 | // }
80 | // });
81 | // 验证成功后
82 | listener.onAuthorize(true); // 验证成功后,回调true
83 | }
84 |
85 | @Override
86 | public String toString() {
87 | final StringBuffer sb = new StringBuffer("LedDevice{");
88 | sb.append("dualColor='").append(dualColor).append('\'');
89 | sb.append(", hardwareVersion='").append(hardwareVersion).append('\'');
90 | sb.append(", powerState=").append(powerState);
91 | sb.append(", nickname='").append(nickname).append('\'');
92 | sb.append('}');
93 | return sb.toString();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/widget/ScanListPopWindow.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.widget;
7 |
8 | import android.app.AlertDialog;
9 | import android.bluetooth.BluetoothDevice;
10 | import android.content.Context;
11 | import android.content.DialogInterface;
12 | import android.support.v7.widget.LinearLayoutManager;
13 | import android.support.v7.widget.RecyclerView;
14 | import android.util.Log;
15 | import android.view.LayoutInflater;
16 | import android.view.View;
17 | import android.view.ViewGroup;
18 | import android.widget.PopupWindow;
19 |
20 | import com.clock.blelib.R;
21 | import com.clock.blelib.adapter.OnItemClickListener;
22 | import com.clock.blelib.adapter.ScanListAdapter;
23 | import com.clock.blelib.util.SystemUtil;
24 |
25 | import java.util.List;
26 |
27 | public class ScanListPopWindow extends PopupWindow implements OnItemClickListener {
28 |
29 | private final String TAG = "ScanListPopWindow";
30 |
31 | private View mMenuView;
32 | private Context mContext;
33 | private RecyclerView.LayoutManager mLayoutManager;
34 | private RecyclerView mRecyclerView;
35 | private ScanListAdapter scanListAdapter;
36 |
37 | public int selectItem = -1;
38 |
39 | public ScanListPopWindow(Context context, View.OnClickListener itemsOnClick) {
40 | super(context);
41 | mContext = context;
42 |
43 | LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
44 | mMenuView = inflater.inflate(R.layout.popwindow_scan_list, null);
45 |
46 | setAdapter();
47 | initData();
48 |
49 | this.setContentView(mMenuView);
50 | this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
51 | this.setHeight(SystemUtil.dip2px(mContext,200));
52 | this.setFocusable(true);
53 | // this.setAnimationStyle(R.style.popwin_anim_style);
54 | }
55 |
56 | private void setAdapter() {
57 | mRecyclerView = mMenuView.findViewById(R.id.rcview_scan_list);
58 | mLayoutManager = new LinearLayoutManager(mContext);
59 | mRecyclerView.setLayoutManager(mLayoutManager);
60 | scanListAdapter = new ScanListAdapter();
61 | mRecyclerView.setAdapter(scanListAdapter);
62 | scanListAdapter.setItemClickListener(this);
63 | }
64 |
65 | private void initData() {
66 | selectItem = -1;
67 | }
68 |
69 | @Override
70 | public void dismiss() {
71 | super.dismiss();
72 | }
73 |
74 | public void updateScanList(List ds) {
75 | scanListAdapter.update(ds);
76 | }
77 |
78 | @Override
79 | public void onItemClick(int position) {
80 | Log.d(TAG, "onItemClick: " + position);
81 | selectItem = position;
82 | dismiss();
83 | }
84 |
85 | @Override
86 | public void onItemLongClick(int position) {
87 | commonDialog(position);
88 | }
89 |
90 | public void commonDialog(final int position) {
91 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
92 | builder.setTitle("警告");
93 | builder.setMessage("你确定删除此定时吗?");
94 | builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
95 | @Override
96 | public void onClick(DialogInterface dialog, int which) {
97 | Log.d(TAG, "点击了确定");
98 | // deleteTimer(position);
99 | }
100 | });
101 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
102 | @Override
103 | public void onClick(DialogInterface dialog, int which) {
104 | Log.d(TAG,"点击了取消");
105 | }
106 | });
107 | AlertDialog alertDialog = builder.create();
108 | alertDialog.show();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/bleissus/blemodel/BLEManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.bleissus.blemodel;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 | import android.util.Log;
10 |
11 | import com.clock.blelib.bleissus.blemodel.bracelet.BraceletDevice;
12 | import com.clock.blelib.bleissus.blemodel.heart.HeaterDataAdapter;
13 | import com.clock.blelib.bleissus.blemodel.heart.HeaterDevice;
14 | import com.clock.blelib.bleissus.blemodel.led.LedDataAdapter;
15 | import com.clock.blelib.bleissus.blemodel.led.LedDevice;
16 | import com.clock.blelib.event.AddNewDeviceEvent;
17 | import com.clock.blelib.event.AddScanDeviceEvent;
18 | import com.clock.blelib.event.BleSendResultEvent;
19 | import com.clock.blelib.event.ConnectDeviceEvent;
20 | import com.clock.blelib.event.updateDeviceInfoEvent;
21 | import com.clock.blelib.util.BytesUtil;
22 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
23 | import com.clock.bluetoothlib.logic.network.connection.BLEBaseManager;
24 | import com.clock.bluetoothlib.logic.network.data.BLEPacket;
25 | import com.clock.bluetoothlib.logic.utils.LogUtil;
26 |
27 | import org.greenrobot.eventbus.EventBus;
28 |
29 | import java.util.HashMap;
30 | import java.util.UUID;
31 |
32 | public class BLEManager extends BLEBaseManager {
33 |
34 | private final String TAG = "BLEManager";
35 |
36 | private static BLEManager instance = new BLEManager();
37 | public static BLEManager getInstance() {
38 | return instance;
39 | }
40 |
41 | @Override
42 | public HashMap onGetDevicesServiceUUID() {
43 | HashMap map = new HashMap();
44 | map.put(BraceletDevice.DEVICE_TYPE_ID, BraceletDevice.SERVICE_UUID);
45 | map.put(HeaterDevice.DEVICE_TYPE_ID, HeaterDevice.SERVICE_UUID);
46 | map.put(LedDevice.DEVICE_TYPE_ID, LedDevice.AD_UUID);
47 |
48 | return map;
49 | }
50 |
51 | @Override
52 | public void onScanOver() {
53 | Log.w(TAG, "onScanOve。。");
54 | }
55 |
56 | @Override
57 | public BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceType) {
58 | if (deviceType == BraceletDevice.DEVICE_TYPE_ID) {
59 | //数据包解析适配器为null,蓝牙设备回应的数据在 onDevicesRespOriginalData(BLEPacket message)
60 | return new BraceletDevice(bluetoothDevice, null);
61 | }
62 | else if (deviceType == HeaterDevice.DEVICE_TYPE_ID) {
63 | // 设置了数据包解析适配器,数据回调在 onDeviceRespSpliceData(BLEPacket message)
64 | return new HeaterDevice(bluetoothDevice, new HeaterDataAdapter());
65 | }
66 | else if (deviceType == LedDevice.DEVICE_TYPE_ID) {
67 | return new LedDevice(bluetoothDevice, new LedDataAdapter());
68 | }
69 | else {
70 | return null;
71 | }
72 | }
73 |
74 | @Override
75 | public void onAddScanDevice(BluetoothDevice bluetoothDevice){
76 | EventBus.getDefault().post(new AddScanDeviceEvent(bluetoothDevice));
77 | }
78 |
79 | @Override
80 | public void onConnectDevice(BLEAppDevice device, int type){
81 | EventBus.getDefault().post(new ConnectDeviceEvent(device, type));
82 | }
83 |
84 | @Override
85 | public void onAddNewDevice(BLEAppDevice device){
86 | EventBus.getDefault().post(new AddNewDeviceEvent(device));
87 | }
88 | @Override
89 | public void onUpdateDeviceInfo(BLEAppDevice device) {
90 | EventBus.getDefault().post(new updateDeviceInfoEvent(device));
91 | }
92 | @Override
93 | public void onDeviceSendResult(String result){
94 | EventBus.getDefault().post(new BleSendResultEvent(result));
95 | }
96 |
97 | @Override
98 | public void onDeviceRespSpliceData(BLEPacket message) {
99 | //定义了数据包解析适配器的设备,通过这个方法回调数据, bleDeviceId标志不同的蓝牙设备,也就是设备的mDeviceId
100 | LogUtil.i(TAG, "onDeviceRespSpliceDat: [" + BytesUtil.BytesToHexStringPrintf(message.bleData) + "] bleDeviceId: " + message.bleDeviceId);
101 | // DataManager.getInstance().DecodeRespData(message.bleData, message.bleDeviceId);
102 |
103 | }
104 |
105 | @Override
106 | public void onDevicesRespOriginalData(BLEPacket message) {
107 | //数据包解析适配器为null的设备,通过这个方法回调数据, bleDeviceId标志不同的蓝牙设备,也就是设备的mDeviceId
108 | LogUtil.v(TAG, "onDevicesRespOriginalDat: [" + BytesUtil.BytesToHexStringPrintf(message.bleData) + "] bleDeviceId: " + message.bleDeviceId);
109 |
110 | }
111 |
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEConnector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 | import android.content.Context;
10 | import android.util.Log;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Random;
14 |
15 | /**
16 | *
17 | */
18 | public final class BLEConnector {
19 |
20 | private final String TAG = "BLEConnector";
21 | private Context mContext;
22 | private Random mRandom = new Random();
23 | private BLEServerListener mBLEServerListener;
24 | private final ArrayList bleConnectingList = new ArrayList<>(); // 正在连接的设备
25 |
26 | private static BLEConnector instance = new BLEConnector();
27 | static BLEConnector getInstance() {
28 | return instance;
29 | }
30 | private BLEConnector() {}
31 |
32 | void init(Context context, BLEServerListener listener) {
33 | mContext = context;
34 | mBLEServerListener = listener;
35 |
36 | Log.w(TAG, "init: BLEConnecto");
37 | }
38 |
39 | /**
40 | * 连接虚拟设备
41 | */
42 | void connToVirtualBleDevice(BluetoothDevice bluetoothDevice) {
43 | connScannedBle(bluetoothDevice);
44 | }
45 |
46 | /**
47 | * 连接自动扫描的设备,一般是开机自动连接那些连接过的设备的过程,先显示,后连接
48 | */
49 | void connToAutoScanBleDevice(BluetoothDevice bluetoothDevice) {
50 | connScannedBle(bluetoothDevice);
51 | mBLEServerListener.onAddPreDevice(bluetoothDevice);
52 | }
53 |
54 | /**
55 | * 连接手动扫描到了的设备
56 | */
57 | int connToScannedBleDevice(BluetoothDevice bluetoothDevice) {
58 | return connScannedBle(bluetoothDevice);
59 | }
60 |
61 | void addConnectingDevice(Integer deviceId) {
62 | synchronized (bleConnectingList) {
63 | if (!bleConnectingList.contains(deviceId)) {
64 | bleConnectingList.add(deviceId);
65 | Log.w(TAG, "addConnectingDevic: " + deviceId);
66 | }
67 | }
68 | }
69 | void removeConnectingDevice(Integer deviceId) {
70 | synchronized (bleConnectingList) {
71 | bleConnectingList.remove(deviceId);
72 | Log.w(TAG, "removeConnectingDevic: " + deviceId);
73 | }
74 | }
75 | int getConnectingDeviceNum() {
76 | synchronized (bleConnectingList) {
77 | return bleConnectingList.size();
78 | }
79 | }
80 |
81 | private int connScannedBle(final BluetoothDevice bluetoothDevice) {
82 |
83 | if (getConnectingDeviceNum() >= 1) { // 有其他设备在连接,此处设定最多同时连1个
84 | Log.w(TAG, "想连接设备,有其他设备在连接,请等待: " + getConnectingDeviceNum());
85 | return -1;
86 | }
87 |
88 | int disConnDevice = BLEServerCentral.getInstance().getDisConnDeviceNum();
89 | if (disConnDevice >0) {
90 | Log.w(TAG, "想连接设备,但是有其他设备 还没释放完,请等待: " + disConnDevice);
91 | return -3;
92 | }
93 |
94 | connBle(bluetoothDevice);
95 | return 0;
96 | }
97 |
98 | void connBle(final BluetoothDevice bluetoothDevice) {
99 | int deviceType = -1;
100 | for(BLEScanDevice scanDevice : BLEScanner.getInstance().deviceScanList) {
101 | if (scanDevice.scanBle.equals(bluetoothDevice)) {
102 | deviceType = scanDevice.deviceType;
103 | }
104 | }
105 | if (deviceType == -1) {
106 | Log.w(TAG, "创建设备前,找不到设备类型");
107 | return;
108 | }
109 |
110 | BLEAppDevice bLEAppDevice = allocateOneBleDevice(bluetoothDevice, deviceType); // 根据类型创建设备
111 | bLEAppDevice.mDeviceTypeId = deviceType;
112 |
113 | bLEAppDevice.connBleSelf();
114 | }
115 |
116 | private BLEAppDevice allocateOneBleDevice(BluetoothDevice bluetoothDevice, int type) {
117 | for(BLELogicDevice value : BLEServerCentral.getInstance().deviceConnectedMap.values()){
118 | if(value.mBleAddress.equals(bluetoothDevice.getAddress())) {
119 | Log.w(TAG, "ble 连接列表 已经包含了这个设备: " + bluetoothDevice.getAddress());
120 | return null;
121 | }
122 | }
123 |
124 | int id = mRandom.nextInt(10000);
125 | Log.w(TAG, "connToBleDevic: 创建新设备分配 id:" + id);
126 |
127 | BLEAppDevice bleDevice = mBLEServerListener.onCreateDevice(bluetoothDevice, type);
128 | bleDevice.initBle(bluetoothDevice, id, mContext, mBLEServerListener, BLEServerCentral.getInstance().mDeviceStatusListener);
129 | return bleDevice;
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/BaseActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib;
7 |
8 | import android.app.Activity;
9 | import android.app.Dialog;
10 | import android.app.ProgressDialog;
11 | import android.content.Intent;
12 | import android.content.pm.ActivityInfo;
13 | import android.content.res.Resources;
14 | import android.os.Bundle;
15 | import android.util.Log;
16 | import android.widget.Toast;
17 |
18 | public class BaseActivity extends Activity {
19 | protected String TAG = "BaseActivity";
20 |
21 | private Dialog mDialog;
22 | private ProgressDialog bigDialog;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
28 | Log.w(TAG, "onCreate.");
29 | }
30 |
31 | @Override
32 | protected void onStart() {
33 | super.onStart();
34 | }
35 |
36 | @Override
37 | protected void onRestart() {
38 | super.onRestart();
39 |
40 | Log.d(TAG, "onRestart..");
41 | }
42 |
43 | @Override
44 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
45 |
46 | super.onRestoreInstanceState(savedInstanceState);
47 | }
48 |
49 | @Override
50 | protected void onResume() {
51 | super.onResume();
52 | // readSystemMember();
53 | }
54 |
55 | @Override
56 | protected void onStop() {
57 | super.onStop();
58 | Log.i(TAG, "onStop...");
59 | }
60 |
61 | @Override
62 | protected void onDestroy() {
63 | super.onDestroy();
64 | showProgressBase(false);
65 | Log.e(TAG, "onDestroy....");
66 | }
67 |
68 | @Override
69 | public Resources getResources() {
70 | Resources resources = super.getResources();
71 | if (resources != null && resources.getConfiguration().fontScale != 1.0f) {
72 | android.content.res.Configuration configuration = resources.getConfiguration();
73 | configuration.fontScale = 1.0f;
74 | resources.updateConfiguration(configuration, resources.getDisplayMetrics());
75 | }
76 | return resources;
77 | }
78 | /**
79 | * 自定义启动动画
80 | */
81 | @Override
82 | public void startActivity(Intent intent) {
83 | super.startActivity(intent);
84 | }
85 | /**
86 | * 自定义启动动画
87 | */
88 | @Override
89 | public void startActivityForResult(Intent intent, int requestCode) {
90 | super.startActivityForResult(intent, requestCode);
91 | }
92 | /**
93 | * 自定义启动动画
94 | */
95 | @Override
96 | public void finish() {
97 | super.finish();
98 | }
99 |
100 | public synchronized void showProgressBase(boolean show) {
101 | if (mDialog != null && mDialog.isShowing()) { // 正在显示,
102 | mDialog.dismiss();
103 | mDialog.cancel();
104 | mDialog = null;
105 | }
106 |
107 | if (show) { // 必须在UI线程
108 | BaseActivity.this.runOnUiThread(new Runnable() {
109 | @Override
110 | public void run() {
111 | mDialog = new Dialog(BaseActivity.this, R.style.framework_dialog_parent_style);
112 | // mDialog.setContentView(R.layout.dialog_framework);
113 | mDialog.setContentView(R.layout.dialog_loading2);
114 | mDialog.setCancelable(true);
115 | mDialog.show();
116 | }
117 | });
118 |
119 | } else {
120 | if (mDialog != null) {
121 | mDialog.dismiss(); // 不必在UI线程
122 | mDialog.cancel();
123 | mDialog = null;
124 | }
125 | }
126 | }
127 |
128 | public synchronized void showBigProgressBase(boolean show, final int content) {
129 | if (bigDialog != null && bigDialog.isShowing()) { // 正在显示,
130 | bigDialog.dismiss();
131 | bigDialog.cancel();
132 | bigDialog = null;
133 | }
134 |
135 | if (show) { // 必须在UI线程
136 | BaseActivity.this.runOnUiThread(new Runnable() {
137 | @Override
138 | public void run() {
139 | if(content == 0) {
140 | bigDialog = new ProgressDialog(BaseActivity.this);
141 | }
142 | else {
143 | bigDialog = new ProgressDialog(BaseActivity.this, content);
144 | }
145 | bigDialog.show();
146 | }
147 | });
148 |
149 | } else {
150 | if (bigDialog != null) {
151 | bigDialog.dismiss(); // 不必在UI线程
152 | bigDialog.cancel();
153 | bigDialog = null;
154 | }
155 | }
156 | }
157 |
158 | public void showToastNoUiBase(final int resId) {
159 | BaseActivity.this.runOnUiThread(new Runnable() {
160 | @Override
161 | public void run() {
162 | Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_SHORT).show();
163 | }
164 | });
165 | }
166 |
167 | public void showToastNoUiBase(final String content) {
168 | BaseActivity.this.runOnUiThread(new Runnable() {
169 | @Override
170 | public void run() {
171 | Toast.makeText(getApplicationContext(), content, Toast.LENGTH_SHORT).show();
172 | }
173 | });
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEBaseDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothGatt;
9 | import android.bluetooth.BluetoothGattCharacteristic;
10 | import android.bluetooth.BluetoothGattDescriptor;
11 | import android.util.Log;
12 |
13 | import com.clock.bluetoothlib.logic.network.DeviceAuthorizeListener;
14 | import com.clock.bluetoothlib.logic.network.DeviceInfoSyncListener;
15 |
16 | import java.util.UUID;
17 |
18 | abstract class BLEBaseDevice {
19 |
20 | private final String TAG = "BLEBaseDevice";
21 | private final UUID UUID_CHARACTERISTIC_CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
22 |
23 | BluetoothGatt mBluetoothGatt;
24 | public int mDeviceId; // 每个ble 分配一个数字id,便于管理(回应数据 回调的handler发送消息,只能是int,用mac地址,6位,则超过了int,无奈之下,只能自己定义一个id)
25 | public BLEState.Connect mBLEConnState = BLEState.Connect.ConnectIdle; // ble的连接状态,只有Active状态才能操作它
26 | public DeviceState mDeviceState = DeviceState.Normal;
27 | public BLEState.ReconnectType mReconnectType = BLEState.ReconnectType.Manual; // 0 自动重连,1 手动重连(强制掉线后手动连它)
28 | public int mDeviceTypeId = 0; // 不同的蓝牙模块类型
29 | public String mBleAddress = ""; // 该ble的mac地址
30 | public String mBleName = "";
31 |
32 | /**
33 | * 连接ble本身
34 | */
35 | abstract void connBleSelf();
36 |
37 | /**
38 | * 手动连接断开的设备
39 | */
40 | abstract int manualConnBle();
41 |
42 | /**
43 | * 强制断开APP与蓝牙设备的连接
44 | */
45 | abstract void ForceDisConn();
46 | abstract void removeSelf();
47 |
48 | /**
49 | * 清空蓝牙连接相关的资源,为自动重连或下一次连接做清理工作
50 | */
51 | abstract void clearBleConnResource();
52 |
53 | /**
54 | * 清空对象占据的内存资源,释放对象
55 | */
56 | abstract void clearMemoryResources();
57 | abstract void writeConfigData(byte[] data);
58 | abstract void writeTransmitData(byte[] data);
59 | abstract boolean enableNotifyCharacter();
60 | /**
61 | * APP发送密码给设备验证,验证通过才能使用此设备,如果没有这个需求,就不必重新这个方法,框架默认校验通过
62 | * @param device
63 | */
64 | public void onAuthorizeDevice(BLEAppDevice device, DeviceAuthorizeListener listener) {
65 | listener.onAuthorize(true);
66 | }
67 | /**
68 | * 密码校验通过后,可以正式通讯,执行通讯前,都需要做的业务,比如获取蓝牙设备的状态信息,配置设备参数等.
69 | * 完成这个方法,蓝牙设备才进入 active 状态,正式被使用
70 | * 如果没有这个需求,就不必重新这个方法,框架默认通过
71 | * @param device
72 | */
73 | public void onDoNecessaryBiz(BLEAppDevice device, DeviceInfoSyncListener listener) {
74 | listener.onInfoSync(1);
75 | }
76 |
77 | public void updateDeviceInfo(Object info) { }
78 |
79 | boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
80 | boolean ret = mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
81 | if (ret) {
82 | Log.w(TAG, " set Notify success");
83 | }
84 | else {
85 | Log.w(TAG, " set Notify fail");
86 | return false;
87 | }
88 |
89 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_CONFIG_DESCRIPTOR);
90 | if(descriptor == null) {
91 | Log.w(TAG, "0 descriptor null ");
92 | return ret;
93 | }
94 | descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE:BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
95 | boolean ret2 = mBluetoothGatt.writeDescriptor(descriptor);
96 | if (ret2) {
97 | Log.w(TAG, "descriptor Notify success");
98 | }
99 | else {
100 | Log.w(TAG, "descriptor Notify fail");
101 | }
102 | return ret2;
103 | }
104 |
105 | /**
106 | * indicate setting
107 | */
108 | boolean setCharacteristicIndication(BluetoothGattCharacteristic characteristic, boolean enable) {
109 | boolean ret = mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
110 | if (ret) {
111 | Log.w(TAG, "1 set Notify success");
112 | }
113 | else {
114 | Log.w(TAG, "1 set Notify fail");
115 | return ret;
116 | }
117 |
118 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_CHARACTERISTIC_CONFIG_DESCRIPTOR);
119 | if(descriptor == null) {
120 | Log.w(TAG, "descriptor null ");
121 | return ret;
122 | }
123 | descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE:BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
124 | boolean ret2 = mBluetoothGatt.writeDescriptor(descriptor);
125 | if (ret2) {
126 | Log.w(TAG, "descriptor INDICATION success");
127 | }
128 | else {
129 | Log.w(TAG, "descriptor INDICATION fail");
130 | }
131 | return ret2;
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/bluetoothlib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 |
24 | # This is a configuration file for ProGuard.
25 | # http://proguard.sourceforge.net/index.html#manual/usage.html
26 | #
27 | # This file is no longer maintained and is not used by new (2.2+) versions of the
28 | # Android plugin for Gradle. Instead, the Android plugin for Gradle generates the
29 | # default rules at build time and stores them in the build directory.
30 |
31 | # Optimizations: If you don't want to optimize, use the
32 | # proguard-android.txt configuration file instead of this one, which
33 | # turns off the optimization flags. Adding optimization introduces
34 | # certain risks, since for example not all optimizations performed by
35 | # ProGuard works on all versions of Dalvik. The following flags turn
36 | # off various optimizations known to have issues, but the list may not
37 | # be complete or up to date. (The "arithmetic" optimization can be
38 | # used if you are only targeting Android 2.0 or later.) Make sure you
39 | # test thoroughly if you go this route.
40 | -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
41 | -optimizationpasses 5
42 | -allowaccessmodification
43 | -dontpreverify
44 |
45 | # The remainder of this file is identical to the non-optimized version
46 | # of the Proguard configuration file (except that the other file has
47 | # flags to turn off optimization).
48 |
49 | -dontusemixedcaseclassnames
50 | -dontskipnonpubliclibraryclasses
51 | -verbose
52 |
53 | -keepattributes *Annotation*
54 | -keep public class com.google.vending.licensing.ILicensingService
55 | -keep public class com.android.vending.licensing.ILicensingService
56 |
57 | # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
58 | -keepclasseswithmembernames class * {
59 | native ;
60 | }
61 |
62 | # keep setters in Views so that animations can still work.
63 | # see http://proguard.sourceforge.net/manual/examples.html#beans
64 | -keepclassmembers public class * extends android.view.View {
65 | void set*(***);
66 | *** get*();
67 | }
68 |
69 | # We want to keep methods in Activity that could be used in the XML attribute onClick
70 | -keepclassmembers class * extends android.app.Activity {
71 | public void *(android.view.View);
72 | }
73 |
74 | # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
75 | -keepclassmembers enum * {
76 | public static **[] values();
77 | public static ** valueOf(java.lang.String);
78 | }
79 |
80 | -keepclassmembers class * implements android.os.Parcelable {
81 | public static final android.os.Parcelable$Creator CREATOR;
82 | }
83 |
84 | -keepclassmembers class **.R$* {
85 | public static ;
86 | }
87 |
88 | # The support library contains references to newer platform versions.
89 | # Don't warn about those in case this app is linking against an older
90 | # platform version. We know about them, and they are safe.
91 | -dontwarn android.support.**
92 |
93 | # Understand the @Keep support annotation.
94 | -keep class android.support.annotation.Keep
95 |
96 | -keep @android.support.annotation.Keep class * {*;}
97 |
98 | -keepclasseswithmembers class * {
99 | @android.support.annotation.Keep ;
100 | }
101 |
102 | -keepclasseswithmembers class * {
103 | @android.support.annotation.Keep ;
104 | }
105 |
106 | -keepclasseswithmembers class * {
107 | @android.support.annotation.Keep (...);
108 | }
109 |
110 | ###########################以下是需要手动的混淆配置协议###############################
111 |
112 |
113 | #-libraryjars "C:\Program Files\Java\jre1.8.0_171\lib\rt.jar"
114 | #-libraryjars "D:\software\android\sdk\platforms\android-28\android.jar"
115 | # 注意:以上两个路径需要将以上路径是自己jar包的位置,需要根据自己情况进行修改,如果报重复配置的错误,注释掉即可
116 |
117 | #代码迭代优化的次数,默认5
118 | -optimizationpasses 5
119 |
120 | #忽略警告
121 | -ignorewarnings
122 | #保证是独立的jar,没有任何项目引用,如果不写就会认为我们所有的代码是无用的,从而把所有的代码压缩掉,导出一个空的jar
123 | -dontshrink
124 | #保护泛型
125 | -keepattributes Signature
126 |
127 | #以下是不需要混淆的文件
128 | #-keep class com.clock.bluetoothlib.logic.network.*
129 |
130 | -keep public class com.clock.bluetoothlib.logic.analysis.* {
131 | public *;
132 | protected *;
133 | }
134 |
135 | -keep public class com.clock.bluetoothlib.logic.network.* {
136 | public *;
137 | protected *;
138 | }
139 |
140 | -keep public class com.clock.bluetoothlib.logic.network.connection.* {
141 | public *;
142 | protected *;
143 | }
144 |
145 | -keep public class com.clock.bluetoothlib.logic.network.data.* {
146 | public *;
147 | protected *;
148 | }
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEDeviceBiz.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 |
9 | import com.clock.bluetoothlib.logic.network.BleLibByteUtil;
10 | import com.clock.bluetoothlib.logic.network.DeviceAuthorizeListener;
11 | import com.clock.bluetoothlib.logic.network.DeviceInfoSyncListener;
12 | import com.clock.bluetoothlib.logic.utils.LogUtil;
13 |
14 | import java.util.Timer;
15 | import java.util.TimerTask;
16 |
17 | final class BLEDeviceBiz {
18 | private final String TAG = "BLEOwnerBiz";
19 | private BLELogicDevice mOwnerDevice;
20 |
21 | private Timer reConnectTimer;
22 |
23 | private BLEState.Reconnect reConnectState = BLEState.Reconnect.ReConnIdle;
24 |
25 | private int ReconnectIntervalTime = 3000; // 断开 间隔 3秒后,才重连
26 | private int ReconnectMaxTime = 10000 + ReconnectIntervalTime;
27 | // private int reconnectCnt = 0;
28 |
29 | BLEDeviceBiz(BLELogicDevice bleDevice) {
30 | mOwnerDevice = bleDevice;
31 | }
32 |
33 | void clearResource() {
34 | mOwnerDevice = null;
35 | if(reConnectTimer != null) {
36 | reConnectTimer.cancel();
37 | }
38 | }
39 |
40 | void clearTimer() {
41 | if(reConnectTimer != null) {
42 | reConnectTimer.cancel();
43 | }
44 | }
45 |
46 | void doConnectedAction() {
47 | LogUtil.w(TAG, "doConnectedActio", (BLEAppDevice) mOwnerDevice);
48 | reConnectState = BLEState.Reconnect.ReConnIdle;
49 | if(reConnectTimer != null) {
50 | LogUtil.w(TAG, "Connected reConnectTimer cancel", (BLEAppDevice) mOwnerDevice);
51 | reConnectTimer.cancel();
52 | reConnectTimer = null;
53 | }
54 | }
55 |
56 | /**
57 | * 设备断线,重连,10秒还没连接成功,不再连。由 BLELogicDevice 类控制
58 | * 重连过程,如果(30秒)一直没有连上,会回调BluetoothProfile.STATE_DISCONNECTED
59 | */
60 | void doDisconnectAction() {
61 | LogUtil.w(TAG, "doDisconnectActio..", (BLEAppDevice) mOwnerDevice);
62 | mOwnerDevice.mBLEConnState = BLEState.Connect.Disconnect;
63 | mOwnerDevice.updateDeviceInfo(null);
64 |
65 | mOwnerDevice.clearBleConnResource();
66 |
67 | // if(reConnectState == BLEState.Reconnect.Reconnecting) {
68 | // LogUtil.w(TAG, "doDisconnectActio: is reconnceting just be Hold ", (BLEAppDevice) mOwnerDevice);
69 | // return;
70 | // }
71 | reConnectState = BLEState.Reconnect.Reconnecting;
72 | if(reConnectTimer != null) {
73 | LogUtil.w(TAG, "reConnectTimer cancel", (BLEAppDevice) mOwnerDevice);
74 | reConnectTimer.cancel();
75 | }
76 | reConnectTimer = new Timer();
77 | // 如果采取5秒重连一次,最终连接成功时,会多次回调callback接口,不可取
78 | reConnectTimer.schedule(new TimerTask() {
79 | @Override
80 | public void run() {
81 | autoReconnectBle();
82 | }
83 | }, ReconnectIntervalTime);
84 | }
85 |
86 | private void autoReconnectBle() {
87 | LogUtil.w(TAG, "auto re conn", (BLEAppDevice) mOwnerDevice);
88 | if (mOwnerDevice.mBLEConnState == BLEState.Connect.ForceDisconnect) {
89 | if(reConnectTimer != null) {
90 | reConnectTimer.cancel();
91 | }
92 | mOwnerDevice.connectFailAction();
93 | LogUtil.w(TAG, "设备已经被强行断开,不再去自动重连", (BLEAppDevice) mOwnerDevice);
94 | return;
95 | }
96 |
97 | mOwnerDevice.connBleSelf();
98 | }
99 |
100 | /**
101 | * 特征都配置好后,进入下一阶段,密码校验
102 | */
103 | void doCharacterEnableAction() {
104 | mOwnerDevice.mBLEConnState = BLEState.Connect.Enable;
105 | mOwnerDevice.onAuthorizeDevice((BLEAppDevice) mOwnerDevice, new DeviceAuthorizeListener() {
106 | @Override
107 | public void onAuthorize(boolean isAuthorize) {
108 | if (isAuthorize) {
109 | mOwnerDevice.mBLEConnState = BLEState.Connect.Authorize;
110 | doAuthorizeSuccessAction();
111 | }
112 | else {
113 | mOwnerDevice.mDeviceStatusListener.onRemove(mOwnerDevice);
114 | }
115 | }
116 | });
117 | }
118 | /**
119 | * APP与蓝牙设备密码校验通过后,进入下一阶段,同步信息
120 | */
121 | private void doAuthorizeSuccessAction() {
122 | // 第一次连接上的设备,才添加到主页设备列表,重连的设备,只需要更新状态
123 | if (mOwnerDevice.isDeviceFirstAdd) {
124 | mOwnerDevice.mBLEServerListener.onAddNewDevice((BLEAppDevice) mOwnerDevice);
125 | mOwnerDevice.isDeviceFirstAdd = false;
126 | }
127 | else {
128 | mOwnerDevice.mBLEServerListener.onReconnectDevice((BLEAppDevice) mOwnerDevice);
129 | }
130 |
131 | // 完成APP与设备的信息同步,设备才处于正常业务交流,可用状态
132 | mOwnerDevice.onDoNecessaryBiz((BLEAppDevice) mOwnerDevice, new DeviceInfoSyncListener() {
133 | @Override
134 | public void onInfoSync(int type) {
135 | if (mOwnerDevice == null) {
136 | LogUtil.e(TAG, "设备已经被删除。。。");
137 | return;
138 | }
139 | LogUtil.w(TAG, "all biz is ok ,set device activity", (BLEAppDevice) mOwnerDevice);
140 | mOwnerDevice.mBLEConnState = BLEState.Connect.Active;
141 | mOwnerDevice.mReconnectType = BLEState.ReconnectType.Auto;
142 | mOwnerDevice.updateDeviceInfo(null);
143 | }
144 | });
145 | }
146 |
147 | private void doConfigRespData(byte[] data) {
148 | LogUtil.i(TAG, "doConfigRespData: " + BleLibByteUtil.BytesToHexStringPrintf(data));
149 |
150 | }
151 | }
152 |
153 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/analysis/RecvCallbackManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.analysis;
7 |
8 | import android.os.Handler;
9 | import android.os.Looper;
10 | import android.os.Message;
11 | import android.util.Log;
12 |
13 | import java.util.concurrent.ConcurrentHashMap;
14 |
15 | class RecvCallbackManager {
16 |
17 | private final String TAG = RecvCallbackManager.class.getSimpleName();
18 | private static final long TIMEOUT = 3000;
19 | private static final int MSG_TIMEOUT = 1;
20 | private static final int MSG_COMMAND = 2;
21 |
22 | private ConcurrentHashMap businessCacheWaitResp = new ConcurrentHashMap<>();
23 | private byte[] timeoutArray;
24 |
25 | private static RecvCallbackManager instance = new RecvCallbackManager();
26 |
27 | private OnDataSendCallBack callBack;
28 |
29 | private RecvCallbackManager() {
30 | timeoutArray = getTimeoutRespMsg();
31 | }
32 |
33 | public static RecvCallbackManager get() {
34 | return instance;
35 | }
36 |
37 | void setCallBack(OnDataSendCallBack callBack) {
38 | this.callBack = callBack;
39 | }
40 |
41 | private byte[] getTimeoutRespMsg() {
42 | return new byte[]{(byte) 0xaa, (byte) 0xee, (byte) 0x00};
43 | }
44 |
45 | private void sendMessageDelayed(int token) {
46 | Message message = handler.obtainMessage(token, MSG_TIMEOUT, 0, timeoutArray);
47 | handler.sendMessageDelayed(message, TIMEOUT);
48 | }
49 |
50 | private void sendMessage(int token, byte[] src) {
51 | Message message = handler.obtainMessage(token, MSG_COMMAND, 0, src);
52 | handler.sendMessage(message);
53 | }
54 |
55 | void parseRecvPackageData(byte[] data, int deviceId) {
56 |
57 | byte[] src = Decode.get().parseRecvData(data, deviceId);
58 | if (src == null) {
59 | return;
60 | }
61 |
62 | int token = src[Decode.get().getDecodeAdapter().getIndexCmd()] & 0xFF;
63 | token = token + (deviceId<<8);
64 | Log.w(TAG, "token 1 : " + token /*String.format("%02x", token)*/);
65 | sendMessage(token, src);
66 | }
67 |
68 | private Handler handler = new Handler(Looper.getMainLooper()) {
69 | public void handleMessage(Message msg) {
70 | final int token = msg.what;
71 | final byte[] message = (byte[]) msg.obj;
72 |
73 | switch (msg.arg1) {
74 | case MSG_COMMAND:
75 | final OnRespDataListener response = businessCacheWaitResp.get(token);
76 | if (response != null) {
77 | removeWaitRespCache(token);
78 | removeMessages(token); // 删除超时队列
79 | response.onResponse(message);
80 | } else {
81 | Log.e(TAG, "!!!!!!!!!!!! get(token) null : " + token);
82 | }
83 | break;
84 |
85 | case MSG_TIMEOUT:
86 | final OnRespDataListener responseTimeout = businessCacheWaitResp.get(token);
87 | if (responseTimeout != null) {
88 | removeWaitRespCache(token);
89 | Log.w(TAG, "remove(token): " + token);
90 | responseTimeout.onResponse(message);
91 | }
92 | break;
93 |
94 | default:
95 | break;
96 | }
97 | }
98 | };
99 |
100 | private void addToWaitRespCache(int token, OnRespDataListener l) {
101 | businessCacheWaitResp.put(token, l);
102 | }
103 |
104 | private void removeWaitRespCache(int token) {
105 | businessCacheWaitResp.remove(token);
106 | }
107 |
108 | private void setReqTimeout(int token) {
109 | sendMessageDelayed(token);
110 | }
111 |
112 | /**
113 | * 回应的数据,直接回调,不走环形缓冲,因为这些数据没有包头。
114 | * 发送一条,回调结束后,才能再发下一条,否则回调方法会被新的一条覆盖掉
115 | * 没有超时检测
116 | *
117 | */
118 | void sendConfigCommand(OnRespDataListener l, byte[] values, int cmdId, int deviceId) {
119 |
120 | // BLEManager.getInstance().sendBLEConfigData(values, bleDeviceId);
121 | callBack.onSendData(values, deviceId);
122 | }
123 |
124 | void sendWriteCommand(OnRespDataListener l, byte[] values, int cmdId, int deviceId) {
125 |
126 | sendDataLogic(l, values, cmdId, deviceId);
127 | }
128 |
129 | private void sendDataLogic(OnRespDataListener l, byte[] values, int cmdId, int deviceId) {
130 |
131 | Log.w(TAG, "token 0 : " + cmdId /*String.format("%02x", cmdId)*/);
132 | addToWaitRespCache(cmdId, l); // 添加响应回调到缓存,等待回调
133 |
134 | sendBytesDate(values, deviceId); // 发送数据
135 |
136 | setReqTimeout(cmdId); // 超时处理
137 | }
138 |
139 | private void sendBytesDate(final byte[] data, int deviceId) {
140 | //一条包字节大于20个,要手动分包
141 | if(data==null){
142 | return;
143 | }
144 | byte[] sendBytes;
145 | sendBytes = data;
146 | int send_time = sendBytes.length / 20;
147 | if (send_time >= 1) {
148 | for (int i = 0; i < send_time; i++) {
149 | byte[] sendByte = new byte[20];
150 | for (int j = 0; j < 20; j++) {
151 | sendByte[j] = sendBytes[20 * i + j];
152 | }
153 | // BLEManager.getInstance().sendBLETransmitData(sendByte, bleDeviceId);
154 | callBack.onSendData(sendByte, deviceId);
155 | try {
156 | Thread.sleep(5);
157 | } catch (InterruptedException e) {
158 | e.printStackTrace();
159 | }
160 | }
161 | if (sendBytes.length % 20 > 0) {
162 | byte[] sendByte = new byte[sendBytes.length % 20];
163 | for (int i = 0; i < sendBytes.length % 20; i++) {
164 | sendByte[i] = sendBytes[sendBytes.length - sendBytes.length % 20 + i];
165 | }
166 | // BLEManager.getInstance().sendBLETransmitData(sendByte, bleDeviceId);
167 | callBack.onSendData(sendByte, deviceId);
168 | }
169 | } else {
170 | // BLEManager.getInstance().sendBLETransmitData(data, bleDeviceId);
171 | callBack.onSendData(data, deviceId);
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEScanner.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothAdapter;
9 | import android.bluetooth.BluetoothDevice;
10 | import android.bluetooth.BluetoothManager;
11 | import android.content.Context;
12 | import android.util.Log;
13 |
14 | import com.clock.bluetoothlib.logic.utils.LogUtil;
15 |
16 | import java.nio.ByteBuffer;
17 | import java.nio.ByteOrder;
18 | import java.util.ArrayList;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.UUID;
22 |
23 | final class BLEScanner implements BluetoothAdapter.LeScanCallback {
24 |
25 | private final String TAG = "BLEScanner";
26 |
27 | private BluetoothManager bluetoothManager;
28 | private BluetoothAdapter bluetoothAdapter;
29 | private UUID SERVICE_UUID;
30 |
31 | BLEState.Scan mBLEScanState = BLEState.Scan.ScanIdle;
32 | // long scanStartTime = 0;
33 | ArrayList deviceScanList = new ArrayList<>();// 扫描到的蓝牙设备
34 | private HashMap deviceServiceUUIDMap;
35 |
36 | private BLEServerListener mListener;
37 |
38 | private static BLEScanner instance = new BLEScanner();
39 | public static BLEScanner getInstance() {
40 | return instance;
41 | }
42 | private BLEScanner() {}
43 |
44 | void init(Context context, HashMap serverUuidMap, boolean isScanFilter, BLEServerListener listener) {
45 |
46 | this.deviceServiceUUIDMap = serverUuidMap;
47 | Log.d(TAG, "init scanner " + serverUuidMap + " " + serverUuidMap.keySet().size() + " " +isScanFilter);
48 | if (serverUuidMap!=null && serverUuidMap.keySet().size()==1 && isScanFilter) {
49 | LogUtil.d(TAG, "serviceUuid: " + serverUuidMap.values().iterator().next());
50 | init(context, serverUuidMap.values().iterator().next(), listener);
51 | }
52 | else {
53 | init(context, null, listener);
54 | }
55 |
56 | }
57 |
58 | private void init(Context context, UUID uuid, BLEServerListener listener) {
59 | SERVICE_UUID = uuid;
60 | mListener = listener;
61 |
62 | bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
63 | if (bluetoothManager != null) {
64 | bluetoothAdapter = bluetoothManager.getAdapter();
65 | }
66 | else {
67 | Log.w(TAG, "init: bluetoothManager null");
68 | }
69 |
70 | if (bluetoothAdapter != null) {
71 | bluetoothAdapter.enable();
72 | }
73 |
74 | Log.w(TAG, "init: BLEServerCentral");
75 | }
76 |
77 | boolean isBleEnable() {
78 | return bluetoothAdapter.isEnabled();
79 | }
80 |
81 | boolean scanBLE() {
82 |
83 | deviceScanList.clear();
84 | if(mBLEScanState == BLEState.Scan.Scanning) {
85 | Log.d(TAG, "scan: is scanning just waiting");
86 | return false;
87 | }
88 | // scanStartTime = System.currentTimeMillis();
89 | Log.d(TAG, "scan: start scan my ");
90 | mBLEScanState = BLEState.Scan.Scanning;
91 |
92 | UUID[] serviceUuids = new UUID[]{SERVICE_UUID};
93 | // UUID[] serviceUuids = new UUID[]{BLELedDevice.SERVICE_UUID, UUID_Service_MH}; // 不能这样,这是全部包含,才扫描到,是与的关系,不是或
94 | if (SERVICE_UUID == null) {
95 | return bluetoothAdapter.startLeScan(this);
96 | }
97 | else {
98 | return bluetoothAdapter.startLeScan(serviceUuids, this);
99 | }
100 | }
101 |
102 | void stopBleScan() {
103 | Log.d(TAG, "stopBleSca... ");
104 | if(mBLEScanState != BLEState.Scan.Scanned) {
105 | bluetoothAdapter.stopLeScan(this);
106 | mBLEScanState = BLEState.Scan.Scanned;
107 | }
108 | }
109 |
110 | @Override
111 | public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecordBytes) {
112 |
113 | // if (deviceScanList.contains(bluetoothDevice)) {
114 | //// Log.d(TAG, "onLeScan: 已经包含这设备了");
115 | // return;
116 | // }
117 | for (BLEScanDevice device: deviceScanList) {
118 | if (device.scanBle.equals(bluetoothDevice)) {
119 | Log.d(TAG, "onLeScan: 已经包含这设备了");
120 | return;
121 | }
122 | }
123 | Log.v(TAG, "onLeScan device: " + bluetoothDevice.getAddress() + " "
124 | + bluetoothDevice.getName() + " " + bluetoothDevice.getBondState() + " 信号强度:" + rssi
125 | + " " + bluetoothDevice.getUuids()); // uuid 为本地缓存的UUID,通常为null
126 |
127 | // Log.i(TAG, "广播包:" + "len: " +scanRecordBytes.length + " 数据:" + BleLibByteUtil.BytesToHexStringPrintf(scanRecordBytes));
128 | // 02 01 06 03 03 f0 ff 0d 09 53 74 65 6c 6c 61 20 53 6d 61 72 74 0d 09 73 74 65 6c 6c 61 20 53 6d 61 72 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
129 | // 02 01 06 03 03 a0 fe 10 08 4e 33 30 32 2d 4c 43 44 2d 30 31 31 34 30 34 07 ff c6 db 0e 6f 7c b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
130 | ParsedAd ad = parseData(scanRecordBytes);
131 | Log.i(TAG, "广播包解析得:" + ad);
132 | if (ad.uuids == null || ad.uuids.size()<=0) {
133 | Log.d(TAG, "扫描到的设备广播无server uuid,忽略它" );
134 | // 处理广播无uuid的设备,一种思路,定义设备类型为-1的无广播设备,在这个地方创建设备,连接(常规)。但也只能创建有一种设备,一个service uuid
135 | return;
136 | }
137 | UUID deviceUuid = ad.uuids.get(0);
138 | if (deviceServiceUUIDMap.containsValue(deviceUuid)) {
139 | int deviceType = -1; // 根据serviceUUID,确定是哪一种设备
140 | for(Integer key : deviceServiceUUIDMap.keySet()){
141 | if (deviceServiceUUIDMap.get(key).equals(deviceUuid)) {
142 | deviceType = key;
143 | break;
144 | }
145 | }
146 | LogUtil.d(TAG, "扫描的 deviceType : " + deviceType);
147 | Log.w(TAG, "onLeScan: 把设备加入到扫描列表 " + bluetoothDevice.toString());
148 | deviceScanList.add(new BLEScanDevice(bluetoothDevice, deviceType));
149 |
150 | mListener.onScannedDevice(bluetoothDevice);
151 | mListener.onAddScanDevice(bluetoothDevice);
152 | }
153 | else {
154 | Log.d(TAG, "扫描到的设备的server uuid 不在范围内" );
155 | return;
156 | }
157 |
158 | }
159 |
160 | public ParsedAd parseData(byte[] adv_data) {
161 | ParsedAd parsedAd = new ParsedAd();
162 | ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN);
163 | while (buffer.remaining() > 2) {
164 | byte length = buffer.get();
165 | if (length == 0)
166 | break;
167 |
168 | byte type = buffer.get();
169 | length -= 1;
170 | switch (type) {
171 | case 0x01: // Flags
172 | parsedAd.flags = buffer.get();
173 | length--;
174 | break;
175 | case 0x02: // Partial list of 16-bit UUIDs
176 | case 0x03: // Complete list of 16-bit UUIDs
177 | case 0x14: // List of 16-bit Service Solicitation UUIDs
178 | while (length >= 2) {
179 | parsedAd.uuids.add(UUID.fromString(String.format(
180 | "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
181 | length -= 2;
182 | }
183 | break;
184 | case 0x04: // Partial list of 32 bit service UUIDs
185 | case 0x05: // Complete list of 32 bit service UUIDs
186 | while (length >= 4) {
187 | parsedAd.uuids.add(UUID.fromString(String.format(
188 | "%08x-0000-1000-8000-00805f9b34fb", buffer.getInt())));
189 | length -= 4;
190 | }
191 | break;
192 | case 0x06: // Partial list of 128-bit UUIDs
193 | case 0x07: // Complete list of 128-bit UUIDs
194 | case 0x15: // List of 128-bit Service Solicitation UUIDs
195 | while (length >= 16) {
196 | long lsb = buffer.getLong();
197 | long msb = buffer.getLong();
198 | parsedAd.uuids.add(new UUID(msb, lsb));
199 | length -= 16;
200 | }
201 | break;
202 | case 0x08: // Short local device name
203 | case 0x09: // Complete local device name
204 | byte sb[] = new byte[length];
205 | buffer.get(sb, 0, length);
206 | length = 0;
207 | parsedAd.localName = new String(sb).trim();
208 | break;
209 | case (byte) 0xFF: // Manufacturer Specific Data
210 | parsedAd.manufacturer = buffer.getShort();
211 | length -= 2;
212 | break;
213 | default: // skip
214 | break;
215 | }
216 | if (length > 0) {
217 | buffer.position(buffer.position() + length);
218 | }
219 | }
220 | return parsedAd;
221 | }
222 |
223 | class ParsedAd {
224 | public byte flags;
225 | public List uuids = new ArrayList<>();
226 | public String localName;
227 | public short manufacturer;
228 |
229 | @Override
230 | public String toString() {
231 | final StringBuffer sb = new StringBuffer("ParsedAd{");
232 | sb.append("flags=").append(flags);
233 | sb.append(", uuids=").append(uuids);
234 | sb.append(", localName='").append(localName).append('\'');
235 | sb.append(", manufacturer=").append(manufacturer);
236 | sb.append('}');
237 | return sb.toString();
238 | }
239 | }
240 |
241 | }
242 |
243 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEServerCentral.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.content.Context;
9 | import android.util.Log;
10 |
11 | import com.clock.bluetoothlib.logic.network.BleLibByteUtil;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Timer;
15 | import java.util.TimerTask;
16 | import java.util.concurrent.ConcurrentHashMap;
17 |
18 | final class BLEServerCentral {
19 |
20 | private final String TAG = "BLEServerCentral";
21 | private Context mContext;
22 | final ConcurrentHashMap deviceConnectedMap = new ConcurrentHashMap<>(); // 已经连接的设备列表,不一定连接成功
23 | // private final ArrayList bleScannedBeToConnList = new ArrayList<>(); // 准备去连接的设备
24 | // private final ArrayList bleConnectingList = new ArrayList<>(); // 正在连接的设备
25 | private final ArrayList beToDisConnBleList = new ArrayList<>(); // 准备去断开连接的设备列表,排队依次断开
26 |
27 | private BLEServerListener mBLEServerListener;
28 |
29 | private Timer toBeDisConnTimer = new Timer();
30 |
31 | // private Handler mainHandler = new Handler(Looper.getMainLooper());
32 |
33 | private final int BleSystemReleaseTime = 1500;
34 | // boolean enableConnect = true;
35 |
36 | private static BLEServerCentral instance = new BLEServerCentral();
37 | static BLEServerCentral getInstance() {
38 | return instance;
39 | }
40 | private BLEServerCentral() {}
41 |
42 | // BLEServerCentral(Context context, BLEServerListener listener) {
43 | // setServerListener(listener);
44 | // init(context);
45 | // }
46 |
47 | void init(Context context, BLEServerListener listener) {
48 | mContext = context;
49 | setServerListener(listener);
50 | // managerConnScannedBleNum();
51 | managerBleDisConn();
52 | Log.w(TAG, "init: BLEServerCentral");
53 | }
54 |
55 | private void setServerListener(BLEServerListener listener) {
56 | mBLEServerListener = listener;
57 | }
58 |
59 | private void managerBleDisConn() { // 系统蓝牙资源,等待1-2s的释放时间后,才能继续连接别的设备
60 | toBeDisConnTimer.schedule(new TimerTask() {
61 | @Override
62 | public void run() {
63 | if (getDisConnDeviceNum() > 0) {
64 | if (beToDisConnBleList.get(0).mBLEConnState == BLEState.Connect.ForceDisconnect) { // 下面停止的设备,释放掉,使能其他设备连接
65 | removeDisConnDevice(beToDisConnBleList.get(0));
66 | }
67 | }
68 | if (getDisConnDeviceNum() > 0) {
69 | beToDisConnBleList.get(0).ForceDisConn(); // 执行后,状态变ForceDisconnect,1s后,检查其状态,然后移除
70 | }
71 | }
72 | }, 1000, BleSystemReleaseTime);
73 | }
74 |
75 |
76 | int manualConnBle(Integer deviceId) {
77 | Log.w(TAG,"想手动重连设备 ble deviceId: " + deviceId);
78 | BLELogicDevice device = deviceConnectedMap.get(deviceId);
79 | if (device == null) {
80 | Log.w(TAG, "想手动重连设备,但是不存在这个设备 " + " " + deviceId);
81 | return -10;
82 | }
83 |
84 | int connectingDevice = BLEConnector.getInstance().getConnectingDeviceNum();
85 | if (connectingDevice > 0) {
86 | Log.w(TAG, "想手动重连设备,但是有其他设备在连,只能退出等待: " + connectingDevice + " " + deviceId);
87 | return -1;
88 | }
89 | int disConnDevice = getDisConnDeviceNum();
90 | if (disConnDevice >0) {
91 | Log.w(TAG, "想手动重连设备,但是有其他设备 还没释放完,只能退出等待: " + disConnDevice + " " + deviceId);
92 | return -3;
93 | }
94 |
95 | if (device.mDeviceState != DeviceState.Normal) {
96 | Log.w(TAG, "想手动重连设备,设备资源还没释放完,只能退出等待: " + " " + deviceId);
97 | return -20;
98 | }
99 |
100 | return device.manualConnBle();
101 | }
102 |
103 | int forceDisConnBle(Integer deviceId) {
104 | Log.w(TAG,"想手动断开设备 ble deviceId: " + deviceId);
105 | BLELogicDevice device = deviceConnectedMap.get(deviceId);
106 | if (device == null) {
107 | Log.w(TAG, "想手动断开设备,但是不存在这个设备 " + " " + deviceId);
108 | return -10;
109 | }
110 |
111 | int connectingDevice = BLEConnector.getInstance().getConnectingDeviceNum();
112 | if (connectingDevice > 0) {
113 | Log.w(TAG, "想手动断开设备,但是有其他设备在连,只能退出等待: " + connectingDevice + " " + deviceId);
114 | return -1;
115 | }
116 | int disConnDevice = getDisConnDeviceNum();
117 | if (disConnDevice >0) {
118 | Log.w(TAG, "想手动断开设备,但是有其他设备 还没释放完,只能退出等待: " + disConnDevice + " " + deviceId);
119 | return -3;
120 | }
121 |
122 | if (device.mBLEConnState != BLEState.Connect.ForceDisconnect) {
123 | Log.d(TAG, "强制断开此设备,加入断开列表:" + deviceId);
124 | device.mBLEConnState = BLEState.Connect.BeToDisconnect;
125 | addDisConnDevice(device);
126 | return 0;
127 | }
128 | else {
129 | Log.d(TAG, "此设备处于断开状态,不必再断开"+ " " + deviceId);
130 | return -20;
131 | }
132 | }
133 |
134 | void removeBle(Integer deviceId) {
135 | Log.w(TAG,"remove ble deviceId: " + deviceId);
136 | BLELogicDevice device = deviceConnectedMap.get(deviceId);
137 | if (device == null) {
138 | Log.e(TAG, "removeBl no this device");
139 | return;
140 | }
141 |
142 | device.removeSelf();
143 | deviceConnectedMap.remove(deviceId);
144 | BLEConnector.getInstance().removeConnectingDevice(deviceId);
145 | System.gc();
146 | }
147 |
148 | // void sendConfigData(byte[] data, int bleDeviceId) {
149 | // Log.e(TAG, bleDeviceId + " sendConfigData: " + BleLibByteUtil.BytesToHexStringPrintf(data));
150 | // BLELogicDevice d = deviceConnectedMap.get(bleDeviceId);
151 | // if(d==null /*|| d.mBLEConnState != BLEState.Active*/) {
152 | // Log.w(TAG, "sendConfigData null or not Active: ");
153 | // return;
154 | // }
155 | // d.writeConfigData(data);
156 | // }
157 |
158 | void sendTransmitData(byte[] data, int deviceId) {
159 | Log.e(TAG, deviceId + " sendTransmitData: " + BleLibByteUtil.BytesToHexStringPrintf(data));
160 | BLELogicDevice d = deviceConnectedMap.get(deviceId);
161 | if(d==null) {
162 | Log.w(TAG, "sendTransmitData null ");
163 | mBLEServerListener.onDeviceSendResult("找不到设备 " + deviceId);
164 | return;
165 | }
166 | if( d.mBLEConnState.index < BLEState.Connect.Enable.index) {
167 | Log.w(TAG, "sendTransmitData not Active: ");
168 | mBLEServerListener.onDeviceSendResult("蓝牙连接失败 " + deviceId);
169 | return;
170 | }
171 | d.writeTransmitData(data);
172 | }
173 |
174 | // private void addConnectingDevice(Integer deviceId) {
175 | // synchronized (bleConnectingList) {
176 | // if (!bleConnectingList.contains(deviceId)) {
177 | // bleConnectingList.add(deviceId);
178 | // Log.w(TAG, "addConnectingDevic: " + deviceId);
179 | // }
180 | // }
181 | // }
182 | // private void removeConnectingDevice(Integer deviceId) {
183 | // synchronized (bleConnectingList) {
184 | // bleConnectingList.remove(deviceId);
185 | // Log.w(TAG, "removeConnectingDevic: " + deviceId);
186 | // }
187 | // }
188 | // int getConnectingDeviceNum() {
189 | // synchronized (bleConnectingList) {
190 | // return bleConnectingList.size();
191 | // }
192 | // }
193 |
194 | private void addDisConnDevice(BLELogicDevice device) {
195 | synchronized (beToDisConnBleList) {
196 | if (!beToDisConnBleList.contains(device)) {
197 | beToDisConnBleList.add(device);
198 | }
199 | }
200 | }
201 | private void removeDisConnDevice(BLELogicDevice device) {
202 | synchronized (beToDisConnBleList) {
203 | beToDisConnBleList.remove(device);
204 | }
205 | }
206 |
207 | int getDisConnDeviceNum() {
208 | synchronized (beToDisConnBleList) {
209 | return beToDisConnBleList.size();
210 | }
211 | }
212 |
213 | /**
214 | * 独立设备跟服务中心的信息交互接口
215 | * 设备的各种状态
216 | */
217 | interface DeviceStatusListener {
218 | void onRemove(BLELogicDevice device);
219 | void onConnected(BLELogicDevice device, Integer deviceId);
220 | void onConnecting(BLELogicDevice device, Integer deviceId);
221 | void onConnectFail(BLELogicDevice device, Integer deviceId);
222 | }
223 |
224 | DeviceStatusListener mDeviceStatusListener = new DeviceStatusListener() {
225 | @Override
226 | public void onRemove(BLELogicDevice device) {
227 | removeBle(device.mDeviceId);
228 | }
229 | @Override
230 | public void onConnected(BLELogicDevice device, Integer deviceId) {
231 | BLEConnector.getInstance().removeConnectingDevice(deviceId);
232 | Log.w(TAG, "connectingBleCn ed : " + BLEConnector.getInstance().getConnectingDeviceNum());
233 | }
234 | @Override
235 | public void onConnecting(BLELogicDevice device, Integer deviceId) {
236 | if (!deviceConnectedMap.containsValue(device)) {
237 | // 设备连接,添加到集合
238 | deviceConnectedMap.put(deviceId, device);
239 | Log.w(TAG, "连接设备集合增加一成员 : " + deviceId);
240 | }
241 | else {
242 | Log.w(TAG, "设备集合已经存在它,不再反复添加 : " + deviceId);
243 | }
244 | BLEConnector.getInstance().addConnectingDevice(deviceId);
245 | Log.w(TAG, "connectingBleCn ing : " + BLEConnector.getInstance().getConnectingDeviceNum() + " " + deviceId);
246 | }
247 | @Override
248 | public void onConnectFail(BLELogicDevice device, Integer deviceId) {
249 | Log.w(TAG, "connect fail : " + BLEConnector.getInstance().getConnectingDeviceNum());
250 | BLEConnector.getInstance().removeConnectingDevice(deviceId);
251 | }
252 | };
253 |
254 | }
255 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/adapter/DeviceListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.blelib.adapter;
7 |
8 | import android.content.DialogInterface;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.Button;
15 | import android.widget.TextView;
16 |
17 | import com.clock.blelib.MainActivity;
18 | import com.clock.blelib.R;
19 | import com.clock.blelib.bleissus.blemodel.BLEManager;
20 | import com.clock.blelib.bleissus.blemodel.bracelet.BraceletDevice;
21 | import com.clock.blelib.bleissus.blemodel.heart.HeaterDevice;
22 | import com.clock.blelib.bleissus.blemodel.led.LedDevice;
23 | import com.clock.blelib.util.WidgetUitl;
24 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
25 | import com.clock.bluetoothlib.logic.network.connection.BLEState;
26 |
27 | import java.util.ArrayList;
28 | import java.util.List;
29 |
30 | public class DeviceListAdapter extends RecyclerView.Adapter {
31 |
32 | private final String TAG = "DeviceListAdapter";
33 | private MainActivity mMain3Activity;
34 | private List mDatas;
35 | private OnItemClickListener mOnItemClickListener = null;
36 |
37 | public DeviceListAdapter() {
38 | }
39 | public DeviceListAdapter(MainActivity activity, List data) {
40 | mMain3Activity = activity;
41 | mDatas = data;
42 | }
43 |
44 | public void update() {
45 | notifyDataSetChanged();
46 | }
47 |
48 | //在Adapter中实现3个方法
49 | @Override
50 | public int getItemCount() {
51 | return mDatas.size();
52 | }
53 |
54 | @Override
55 | public VH onCreateViewHolder(ViewGroup parent, int viewType) {
56 | if (parent == null) {
57 | Log.e(TAG, "parent null");
58 | return null;
59 | }
60 | View v;
61 | v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_device_list_device, parent, false);
62 |
63 | return new VH(v);
64 | }
65 |
66 | @Override
67 | public void onBindViewHolder(final VH holder, final int position) {
68 | Log.v(TAG, "position: " + position + mDatas.get(position));
69 |
70 | String name = "default";
71 | if(mDatas.get(position).mDeviceTypeId == HeaterDevice.DEVICE_TYPE_ID) {
72 | name = mDatas.get(position).mBleName + " " + "--Heater";
73 | }
74 | else if(mDatas.get(position).mDeviceTypeId == LedDevice.DEVICE_TYPE_ID) {
75 | name = mDatas.get(position).mBleName + " " + "--Led";
76 | }
77 | holder.tv_name.setText(name);
78 |
79 | if (mDatas.get(position).mBLEConnState == BLEState.Connect.Active) {
80 | holder.btn_conn.setText("断开");
81 | }
82 | else {
83 | holder.btn_conn.setText("连接");
84 | }
85 |
86 | holder.btn_send.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View v) {
89 | byte[] value = new byte[]{(byte) 0xAA, 0x00, 0x00};
90 | // 不同设备,可能数据协议不同
91 | if (mDatas.get(position).mDeviceTypeId == BraceletDevice.DEVICE_TYPE_ID
92 | || mDatas.get(position).mDeviceTypeId == HeaterDevice.DEVICE_TYPE_ID) {
93 |
94 | value = new byte[]{(byte) 0xAA, 0x0d, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};//
95 | }
96 | else if(mDatas.get(position).mDeviceTypeId == LedDevice.DEVICE_TYPE_ID ) {
97 | value = new byte[]{(byte) 0xAA, 0x0d, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00};//
98 | }
99 |
100 | BLEManager.getInstance().sendBLETransmitData(value, mDatas.get(position).mDeviceId);
101 | }
102 | });
103 |
104 | holder.btn_conn.setOnClickListener(new View.OnClickListener() {
105 | @Override
106 | public void onClick(View v) {
107 | if (mDatas.get(position).mBLEConnState == BLEState.Connect.Active) {
108 | manualDisConnDevice(position);
109 | }
110 | else {
111 | manualConnDevice(position);
112 | }
113 | }
114 | });
115 |
116 | holder.btn_delete.setOnClickListener(new View.OnClickListener() {
117 | @Override
118 | public void onClick(View v) {
119 | showDeleteDialog(position);
120 | }
121 | });
122 |
123 | }
124 |
125 | public void setItemClickListener(OnItemClickListener lis) {
126 | mOnItemClickListener = lis;
127 | }
128 |
129 |
130 | private void showDeleteDialog(final int pos) {
131 |
132 | WidgetUitl.showLiteCommDialog(mMain3Activity,
133 | "delete device", "Sure to delete this device?", new DialogInterface.OnClickListener() {
134 | @Override
135 | public void onClick(DialogInterface dialog, int which) {
136 | BLEManager.getInstance().removeDevice(mDatas.get(pos).mDeviceId);
137 | mDatas.remove(pos);
138 | notifyDataSetChanged();
139 | }
140 | });
141 | }
142 |
143 | private void manualConnDevice(final int position) {
144 | mMain3Activity.mHandlerList.removeCallbacksAndMessages(null);
145 | mMain3Activity.mHandlerList.postDelayed(new Runnable() {
146 | @Override
147 | public void run() {
148 | Log.e(TAG, "延迟200后,尝试去手动重连:" + mDatas.get(position).mBleAddress);
149 | int ret;
150 | if (mDatas.get(position).isVirtualDevice == true) {
151 | List temp = new ArrayList();
152 | temp.add(mDatas.get(position).mBleAddress);
153 | ret = BLEManager.getInstance().autoScanConnVirtualDevice(temp);
154 | }
155 | else {
156 | ret = BLEManager.getInstance().manualConnDevice(mDatas.get(position).mDeviceId);
157 | }
158 |
159 | if (ret == -3) {
160 | Log.w(TAG, "Other device releasing ,please wait");
161 | mMain3Activity.showToastNoUiBase("ble system is releasing ,please wait!");
162 | return;
163 | }
164 | if (ret == -20) {
165 | Log.w(TAG, "device is releasing ,please wait!");
166 | mMain3Activity.showToastNoUiBase("device is releasing ,please wait!");
167 | // mMain3Activity.mCurrDevice.mBLEConnState = BLEState.Connect.Active;
168 | // notifyDataSetChanged();
169 | return;
170 | }
171 | if (ret == -30) {
172 | Log.w(TAG, "device is connecting ,please wait!");
173 | mMain3Activity.showToastNoUiBase("device is connecting ,please wait!");
174 | return;
175 | }
176 | if (ret == -10) {
177 | Log.w(TAG, "device is Invalid ,please wait!");
178 | mMain3Activity.showToastNoUiBase("device error ,please retry!");
179 | return;
180 | }
181 |
182 | if (ret != 0) {
183 | Log.w(TAG, "Other device is connecting ,please wait!");
184 | mMain3Activity.showToastNoUiBase("Other device is connecting ,please wait!");
185 | return;
186 | }
187 | mMain3Activity.showProgressBase(true);
188 | }
189 | }, 200);
190 | }
191 |
192 | private void manualDisConnDevice(final int position) {
193 | WidgetUitl.showLiteCommDialog(mMain3Activity,
194 | "disconnect device", "Sure to disconnect this device?", new DialogInterface.OnClickListener() {
195 | @Override
196 | public void onClick(DialogInterface dialog, int which) {
197 | int ret;
198 | ret = BLEManager.getInstance().forceDisConnDevice(mDatas.get(position).mDeviceId);
199 | if (ret == -3) {
200 | Log.w(TAG, "Other device releasing ,please wait");
201 | mMain3Activity.showToastNoUiBase("ble system is releasing ,please wait!");
202 | return;
203 | }
204 | if (ret == -20) {
205 | Log.w(TAG, "Other device is releasing ,please wait!");
206 | mMain3Activity.showToastNoUiBase("device is releasing ,please wait!");
207 | return;
208 | }
209 | if (ret == -30) {
210 | Log.w(TAG, "Other device is connecting ,please wait!");
211 | mMain3Activity.showToastNoUiBase("device is connecting ,please wait!");
212 | return;
213 | }
214 | if (ret == -10) {
215 | Log.w(TAG, "device is Invalid ,please wait!");
216 | mMain3Activity.showToastNoUiBase("device error ,please retry!");
217 | return;
218 | }
219 |
220 | if (ret != 0) {
221 | Log.w(TAG, "Other device is connecting ,please wait!");
222 | mMain3Activity.showToastNoUiBase("Other device is connecting ,please wait!");
223 | return;
224 | }
225 | notifyDataSetChanged();
226 | }
227 | });
228 | }
229 |
230 |
231 | static class VH extends RecyclerView.ViewHolder{
232 | private final TextView tv_name;
233 | private final Button btn_send;
234 | private final Button btn_conn;
235 | private final Button btn_delete;
236 |
237 | private VH(View v) {
238 | super(v);
239 | tv_name = v.findViewById(R.id.tv_name);
240 | btn_send = v.findViewById(R.id.btn_send);
241 | btn_conn = v.findViewById(R.id.btn_conn);
242 | btn_delete = v.findViewById(R.id.btn_delete);
243 | }
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/connection/BLEBaseManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.connection;
7 |
8 | import android.bluetooth.BluetoothDevice;
9 | import android.content.Context;
10 | import android.util.Log;
11 |
12 | import com.clock.bluetoothlib.logic.network.data.BLEAllDispatcher;
13 | import com.clock.bluetoothlib.logic.network.data.BLEPacket;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Timer;
19 | import java.util.TimerTask;
20 | import java.util.UUID;
21 |
22 | public abstract class BLEBaseManager implements BLEServerListener {
23 |
24 | private final String TAG = "BLEBaseManager";
25 | // 只能初始化一次
26 | private boolean isBleInit = false;
27 | // 预防系统蓝牙还没启动完成,就执行扫描的情况
28 | private int autoConnCnt = 0;
29 |
30 | private BLEServerCentral mBLEServerCentral;
31 | private BLEScanner mBLEScanner;
32 | private BLEConnector mBLEConnector;
33 | private Timer scanTimer; // 扫描定时器
34 | /**
35 | * APP曾连接过的,APP启动后,就要自动去连的设备
36 | */
37 | private List bleAutoToConn = new ArrayList();
38 | /**
39 | * APP自动扫描、连接指定的设备,包括1:APP启动后,自动连接之前连接过的设备;
40 | */
41 | private boolean isAutoScanConnMode = false;
42 | /**
43 | * 没有被扫描出来,但是之前连接过,需要显示在列表,这样的设备定义为虚拟设备,
44 | * 在界面手动连接虚拟设备,需启动扫描,扫描到再连接,非虚拟设备,不需要再启动扫描,
45 | * 连接虚拟设备,先扫描,如果在扫描过程中,其他设备要去连,得先等虚拟设备连接完
46 | */
47 | private boolean isConnVirtualDevice = false;
48 |
49 | private int autoScanConnDeviceCnt = 0;
50 | /**
51 | * 自动扫描、连接模式,连接设备的个数
52 | * 默认只连一个,其他直接显示
53 | */
54 | protected int autoScanConnMaxNum = 1;
55 | /**
56 | * 通过设备的serviceUUID,过滤扫描结果
57 | * 1、只有一种类型的设备serviceUUID,默认是过滤的,扫描结果只显示这种设备;
58 | * 2、多种设备,则会把周围的所有蓝牙设备扫描出来。蓝牙api,目前无法同时过滤多种serviceUUID,因为源码中过滤时是与的关系,不是或。
59 | * 遇到过一些蓝牙模块有问题,过滤则无法扫描出来,则需要设置此变量为false;
60 | */
61 | protected boolean isScanFilter = true;
62 | /**
63 | * 扫描停止时间,默认10s,单位ms
64 | */
65 | protected int scanTimeOut = 10000;
66 |
67 | public BLEBaseManager() {}
68 |
69 | /**
70 | * 自定义了多种设备,把这些设备的deviceTypeId、serviceUUID,添加对这个map;
71 | * 框架扫描到设备后,解析广播数据得到serviceUUID,分辨是哪种类型的设备,然后通过onCreateDevice(BluetoothDevice bluetoothDevice, int deviceTypeId),回调给用户,
72 | * 用户在接口里,根据deviceTypeId new一个设备返回给框架
73 | * 如果广播不携带uuid,该框架忽略此设备
74 | * @return 非 null
75 | */
76 | protected abstract HashMap onGetDevicesServiceUUID();
77 |
78 | /**
79 | * 开启蓝牙使能后,第一步调用此方法
80 | * @param context
81 | */
82 | public synchronized void initBle(Context context) {
83 | if(!isBleInit) {
84 | isBleInit = true;
85 | mBLEScanner = BLEScanner.getInstance();
86 | mBLEScanner.init(context,onGetDevicesServiceUUID(),isScanFilter,this);
87 |
88 | mBLEServerCentral = BLEServerCentral.getInstance();
89 | mBLEServerCentral.init(context, this);
90 |
91 | mBLEConnector = BLEConnector.getInstance();
92 | mBLEConnector.init(context, this);
93 |
94 | BLEAllDispatcher.getInstance().mBLEServerListener = this;
95 | }
96 | }
97 |
98 | public boolean isBleEnable() {
99 | return mBLEScanner.isBleEnable();
100 | }
101 |
102 | /**
103 | * 手动扫描蓝牙设备
104 | */
105 | public synchronized boolean scanBLEDeviceManual() {
106 | bleAutoToConn.clear(); // 手动去扫描,把开机自动扫描连接的列表清空,否则,这样也去连他们
107 | boolean ret = mBLEScanner.scanBLE();
108 | if (ret) {
109 | managerScanTime(scanTimeOut);
110 | }
111 | return ret;
112 | }
113 |
114 | public synchronized void stopScanDevice() {
115 | if(mBLEScanner.mBLEScanState != BLEState.Scan.Scanned) {
116 | mBLEScanner.stopBleScan();
117 | onScanOver();
118 | }
119 | }
120 |
121 | /**
122 | * 连接扫描到的某个蓝牙设备
123 | * @param bluetoothDevice
124 | */
125 | public int connScannedBleDevice(final BluetoothDevice bluetoothDevice) {
126 | abortScanTimer();
127 | stopScanDevice(); // 正常添加连接 连接前,断开扫描,这样快一点
128 | return mBLEConnector.connToScannedBleDevice(bluetoothDevice);
129 | }
130 |
131 | /**
132 | * 自动扫描连接 曾经连接过的设备,只有在启动APP时执行的操作
133 | */
134 | public void autoScanConnConnectedDevice(List connectedMac) {
135 | bleAutoToConn.clear();
136 | bleAutoToConn.addAll(connectedMac);
137 | autoScanConnDeviceCnt = 0;
138 | isAutoScanConnMode = true;
139 | autoScanDevice();
140 | }
141 |
142 | /**
143 | * 虚拟设备:APP曾连接过,但现在扫描不到的设备,没办法给它分配一个实体对象。手动去连它时,得启动扫描。
144 | * 自动扫描连接虚拟设备,每次只有一个虚拟设备去连接
145 | */
146 | public int autoScanConnVirtualDevice(List connectedMac) {
147 | int connectingDevice = mBLEConnector.getConnectingDeviceNum();
148 | if (connectingDevice > 0) {
149 | Log.w(TAG, "1 想手动重连设备,但是有其他设备在连,只能退出等待: " + connectingDevice + " " + connectedMac);
150 | return -1;
151 | }
152 | int disConnDevice = mBLEServerCentral.getDisConnDeviceNum();
153 | if (disConnDevice >0) {
154 | Log.w(TAG, "1 想手动重连设备,但是有其他设备 还没释放完,只能退出等待: " + disConnDevice + " " + connectedMac);
155 | return -3;
156 | }
157 | if (isConnVirtualDevice) {
158 | Log.w(TAG, "想手动重连设备,但是正在连虚拟设备,请等待 " + " " + connectedMac);
159 | return -30;
160 | }
161 | bleAutoToConn.clear();
162 | bleAutoToConn.addAll(connectedMac);
163 |
164 | isConnVirtualDevice = true;
165 |
166 | autoScanDevice();
167 | return 0;
168 | }
169 |
170 | /**
171 | * 系统蓝牙还没启动完成,就执行扫描,多试几次,直到蓝牙启动完成
172 | */
173 | private void autoScanDevice() {
174 | if(!mBLEScanner.isBleEnable()) {
175 | Log.w(TAG, "scanBLEDevic: ble not enable");
176 | autoConnCnt++;
177 | }
178 | else {
179 | autoConnCnt = 0;
180 | }
181 |
182 | new Timer().schedule(new TimerTask() {
183 | @Override
184 | public void run() {
185 | if(autoConnCnt == 0) {
186 | Log.w(TAG, "蓝牙初始化 s后,开始扫描设备" );
187 | boolean ret = mBLEScanner.scanBLE();
188 | if (ret) {
189 | managerScanTime(scanTimeOut);
190 | }
191 | }
192 | else {
193 | if(autoConnCnt >5 ) {
194 | autoConnCnt = 0;
195 | Log.w(TAG, "蓝牙还没启动成功,bu zai 重来" );
196 | return;
197 | }
198 | Log.w(TAG, "蓝牙还没启动成功,再重来" );
199 | autoScanDevice();
200 | }
201 | }
202 | }, 500);
203 | }
204 |
205 | /**
206 | * 管理扫描时间, n 秒后终止扫描
207 | */
208 | private void managerScanTime(int timeOut) {
209 | if(scanTimer != null) {
210 | Log.w(TAG, "scanTimer cancel");
211 | scanTimer.cancel();
212 | }
213 | scanTimer = new Timer();
214 | scanTimer.schedule(new TimerTask() {
215 | @Override
216 | public void run() {
217 | stopScanDevice();
218 | bleAutoToConn.clear();
219 | isAutoScanConnMode = false;
220 | if (isConnVirtualDevice) {
221 | isConnVirtualDevice = false;
222 | onScanConnVirtualDevice(0);
223 | }
224 | }
225 | }, timeOut);
226 | }
227 |
228 | private void abortScanTimer() {
229 | if(scanTimer != null) {
230 | Log.w(TAG, "scanTimer abortScanTim");
231 | scanTimer.cancel();
232 | scanTimer = null;
233 | }
234 | }
235 |
236 | /**
237 | * 手动连接断开的设备
238 | * @param deviceId
239 | * @return
240 | */
241 | public int manualConnDevice(final Integer deviceId) {
242 | if (isConnVirtualDevice) {
243 | Log.w(TAG, "想手动重连设备,但是正在连虚拟设备,请等待 " + " " + deviceId);
244 | return -30;
245 | }
246 | return mBLEServerCentral.manualConnBle(deviceId);
247 | }
248 |
249 | /**
250 | * 删除蓝牙设备
251 | * @param deviceId
252 | */
253 | public void removeDevice(Integer deviceId) {
254 | mBLEServerCentral.removeBle(deviceId);
255 | }
256 |
257 | /**
258 | * 断开蓝牙设备的连接
259 | * @param deviceId
260 | */
261 | public int forceDisConnDevice(final Integer deviceId) {
262 | if (isConnVirtualDevice) {
263 | Log.w(TAG, "想手动重连设备,但是正在连虚拟设备,请等待 " + " " + deviceId);
264 | return -30;
265 | }
266 | return mBLEServerCentral.forceDisConnBle(deviceId);
267 | }
268 |
269 | public boolean hasDeviceConnecting() {
270 | int cnt = mBLEConnector.getConnectingDeviceNum();
271 | if (cnt == 0) {
272 | return false;
273 | }
274 | return true;
275 | }
276 |
277 | /**
278 | * 发送蓝牙透传数据
279 | * @param data 数据
280 | * @param deviceId 蓝牙设备mDeviceId
281 | */
282 | public void sendBLETransmitData(final byte[] data, final int deviceId) {
283 | mBLEServerCentral.sendTransmitData(data, deviceId);
284 | }
285 |
286 | // public void sendBLEConfigData(final byte[] data, final int bleDeviceId) {
287 | // mBLEServerCentral.sendConfigData(data, bleDeviceId);
288 | // }
289 |
290 | @Override
291 | public final void onScannedDevice(BluetoothDevice bluetoothDevice) {
292 | if (bleAutoToConn.contains(bluetoothDevice.getAddress())) {
293 | Log.d(TAG, "自动连接ble 包含其中");
294 | if (isConnVirtualDevice) {
295 | abortScanTimer();
296 | stopScanDevice(); // 每次只连一个虚拟设备,扫描到了,就停止扫描了,下一步去连了
297 | mBLEConnector.connToVirtualBleDevice(bluetoothDevice);
298 | isConnVirtualDevice = false;
299 | }
300 | else if(isAutoScanConnMode) {
301 | autoScanConnDeviceCnt++;
302 | if (autoScanConnDeviceCnt >= autoScanConnMaxNum) {
303 | Log.w(TAG, "开机自动扫描连接模式 ,已经连了 " + autoScanConnMaxNum + " 个设备,断开扫描,停止其他连接");
304 | abortScanTimer();
305 | stopScanDevice();
306 | isAutoScanConnMode = false; // 扫描结束,模式去掉
307 | }
308 |
309 | if (autoScanConnDeviceCnt <= autoScanConnMaxNum) {
310 | mBLEConnector.connToAutoScanBleDevice(bluetoothDevice);
311 | }
312 | }
313 | }
314 | }
315 |
316 | @Override
317 | public abstract void onScanOver();
318 |
319 | @Override
320 | public abstract void onAddScanDevice(BluetoothDevice bluetoothDevice);
321 |
322 | @Override
323 | public void onScanConnVirtualDevice(int type) {
324 |
325 | }
326 |
327 | @Override
328 | public abstract BLEAppDevice onCreateDevice(BluetoothDevice bluetoothDevice, int deviceType);
329 |
330 | @Override
331 | public abstract void onConnectDevice(BLEAppDevice device, int type);
332 | @Override
333 | public void onCharacterMatch(BluetoothDevice device, int type) {
334 |
335 | }
336 | @Override
337 | public void onAddPreDevice(BluetoothDevice device){}
338 |
339 | @Override
340 | public abstract void onAddNewDevice(BLEAppDevice device);
341 |
342 | @Override
343 | public void onReconnectDevice(BLEAppDevice device) {
344 |
345 | }
346 | @Override
347 | public abstract void onUpdateDeviceInfo(BLEAppDevice device);
348 |
349 | @Override
350 | public abstract void onDeviceSendResult(String result);
351 |
352 | @Override
353 | public abstract void onDevicesRespOriginalData(BLEPacket message);
354 | @Override
355 | public abstract void onDeviceRespSpliceData(BLEPacket message);
356 | }
357 |
--------------------------------------------------------------------------------
/bluetoothlib/src/main/java/com/clock/bluetoothlib/logic/network/data/DataCircularBuffer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C), 2019. by [clock - Individual developer] ,All rights reserved.
3 | * The article is original, writing is also the strength to live, reference please indicate the source and author.
4 | */
5 |
6 | package com.clock.bluetoothlib.logic.network.data;
7 |
8 | import android.util.Log;
9 |
10 |
11 | import com.clock.bluetoothlib.logic.network.BleLibByteUtil;
12 | import com.clock.bluetoothlib.logic.utils.LogUtil;
13 |
14 | import java.util.Arrays;
15 | import java.util.LinkedList;
16 | import java.util.List;
17 |
18 | /**
19 | * 数据环形缓冲器
20 | * 数据包结构必须是:包标识(1字节)+ 其他(n字节)+ 内容长度(1字节)+内容(n字节)+其他(n字节)
21 | * 此算法,根据包标志、包长度、包结构,挑出一个完整数据包
22 | */
23 | public final class DataCircularBuffer {
24 | private final String TAG2 = "CircularBufferBase";
25 | private byte[] buffer;
26 | private int readPos;
27 | private int writePos;
28 | private int capacity; // 容量
29 | private int readableSize; // 当前可读元素个数
30 | private LinkedList packageQueue = new LinkedList();
31 | private BLEAllDispatcher mDataDispatchCenter = null;
32 | private byte[] onePackage;
33 |
34 | private int deviceId;
35 | private DataParserAdapter adapter;
36 |
37 | public DataCircularBuffer(int cap, int id, DataParserAdapter adapter) {
38 | deviceId = id;
39 | mDataDispatchCenter = BLEAllDispatcher.getInstance();
40 | this.adapter = adapter;
41 | if (this.adapter == null) {
42 | d("没有数据解析适配器,不用初始化缓冲区");
43 | return;
44 | }
45 | buffer = new byte[cap];
46 | readPos = writePos = 0;
47 | readableSize = 0;
48 | capacity = cap;
49 | d("BLELedBuffer capacity = " + capacity);
50 | mDataDispatchCenter.startRun();
51 | }
52 |
53 | public void destroyBuffer() {
54 | reset();
55 | packageQueue = null;
56 | buffer = null;
57 | capacity = 0;
58 | mDataDispatchCenter = null;
59 | // mDataDispatchCenter.stopRun();
60 | }
61 |
62 | void reset() {
63 | readPos = 0;
64 | writePos = 0;
65 | readableSize = 0;
66 | packageQueue.clear();
67 | }
68 |
69 | private void printfBuffer() {
70 | Log.e(TAG2, "缓冲区所有数据 从缓冲头开始:" + BleLibByteUtil.BytesToHexStringPrintf(buffer));
71 | int index = readPos;
72 | StringBuilder builder = new StringBuilder();
73 | for (int i = 0; i < capacity; i++) {
74 | if (index >= capacity) {
75 | builder.append(String.format("%02x", buffer[(index - capacity)]) + " ");
76 | } else {
77 | builder.append(String.format("%02x", buffer[index]) + " ");
78 | }
79 | index++;
80 | }
81 | Log.e(TAG2, "缓冲区所有数据 从读取位开始:" + builder.toString());
82 | }
83 |
84 | /**
85 | * @param len
86 | * @return 写入成功返回0,其他-1
87 | */
88 | private int writeArray(byte[] des, int len) {
89 | if (!isSpaceEnough(len)) {
90 | return -1;
91 | }
92 | // d("写之前的size: " + readableSize + " writePos: " + writePos);
93 | for (int i = 0; i < len; i++) {
94 | writeByte(des[i]);
95 | }
96 | // d("写后的size: " + readableSize + " writePos: " + writePos);
97 | return 0;
98 | }
99 |
100 | private void writeByte(byte a) {
101 | if (writePos == capacity) {
102 | // Log.w(TAG2,"写了一圈,从头开始");
103 | writePos = 0;
104 | d("writePos set 0");
105 | }
106 | buffer[writePos++] = a;
107 | // buf[writePos++ % capacity] = a;
108 | ++readableSize;
109 | }
110 |
111 | /**
112 | * 从缓冲区读走len长的数据
113 | * @param len
114 | * @return
115 | */
116 | private byte[] readArray(int len, byte mark) {
117 | if (IsEmpty() || len > capacity || len > readableSize) {
118 | d("ReadArray error!");
119 | return null;
120 | }
121 | // d("读之前的readableSize: " + readableSize + " readPos: " + readPos);
122 | byte[] temp = new byte[len];
123 | for (int i = 0; i < len; i++) {
124 | temp[i] = readByte();
125 | }
126 | // d("读后的readableSize: " + readableSize + " readPos: " + readPos);
127 | // Log.v(TAG2, "readArray: " + BytesUtil.BytesToHexStringPrintf(temp));
128 | if (temp[0] != mark) {
129 | Log.e(TAG2, "数据第一个字节不是正确的头: " + BleLibByteUtil.BytesToHexStringPrintf(temp));
130 | int j = 0;
131 | for (j = 0; j < len; j++) {
132 | if (temp[j] == mark) {
133 | break;
134 | }
135 | }
136 | Log.i(TAG2, "size: " + temp.length + " len:" + len + " j: " + j);
137 | byte[] temp2 = Arrays.copyOfRange(temp, j, len); // 不包含最后一个字节,不用len-1
138 | Log.e(TAG2, "截取后的数组:" + BleLibByteUtil.BytesToHexStringPrintf(temp2));
139 | return temp2;
140 | }
141 | return temp;
142 | }
143 |
144 | private byte readByte() {
145 | if (readPos == capacity) {
146 | // Log.w(TAG2,"读一圈了,从头开始");
147 | readPos = 0;
148 | d("readPos set 0");
149 | }
150 | --readableSize;
151 | return buffer[readPos++];
152 | }
153 |
154 | private boolean IsEmpty() {
155 | if (readableSize == 0) {
156 | d("IsEmpty!");
157 | return true;
158 | }
159 | return false;
160 | }
161 |
162 | private int getWriteAndReadDis() {
163 | if (writePos < readPos) {
164 | return ((capacity + writePos) - readPos);
165 | } else {
166 | return (writePos - readPos);
167 | }
168 | }
169 |
170 | private int getDistCurrPosToEnd(int currPos) { // 必须同步加锁,单线程执行
171 | if (writePos < currPos) {
172 | return ((capacity + writePos) - currPos);
173 | } else {
174 | return (writePos - currPos);
175 | }
176 | }
177 |
178 | /**
179 | * 是否有足够的空间写入新数据
180 | *
181 | * @param writeLen
182 | * @return
183 | */
184 | private boolean isSpaceEnough(int writeLen) {
185 | if ((readableSize + writeLen) > capacity) {
186 | d("IsWriteEnough!");
187 | return false;
188 | }
189 | return true;
190 | }
191 |
192 | /**
193 | * 提取所有包
194 | * 缓冲区写入了很多数据,从这些数据中,把有效包一个个提取出来
195 | * 可能包含一个,可能多个,可能半个,可能多个加半个,可能一个也没有都是辣鸡数据
196 | *
197 | * @param mark 包标识
198 | * @return 返回有效包队列
199 | */
200 | private List extractAllPackage(byte mark) {
201 | packageQueue.clear(); // 如果数据还没处理完,又来新数据,就会把旧数据清空,可能导致错误,需同步,但是这种情况目前还没出现
202 | seekForPackage(mark);
203 | return packageQueue;
204 | }
205 |
206 | /**
207 | * 找寻符合包定义的数据段
208 | * 根据包标志,包长度,包结构,找到符合包定义数据段
209 | * @param mark 目标标志
210 | * @return
211 | */
212 | private int seekForPackage(byte mark) {
213 | int number = 0;
214 | int cnt = readableSize;
215 | int pos = readPos;
216 | int contentLen = 0;
217 | int packageLen = 0;
218 |
219 | for (int i = 0; i < cnt; i++) {
220 | if (pos >= capacity) {
221 | // Log.w(TAG2,"已经读了一圈");
222 | pos = 0;
223 | }
224 | // 搜索到包标志
225 | if (buffer[pos] == mark) { // pos大于buff容量的判断,前两行代码已经判断了
226 | // 包头到最后面的数据的长度, 判断后面是否还有数据,携带包长度
227 | if (getDistCurrPosToEnd(pos) > adapter.getDistMarkToContent()) {
228 | if (pos + adapter.getDistMarkToContent() >= capacity) { // 包标志跟长度值,在环形缓冲区的临界两边
229 | contentLen = buffer[(pos + adapter.getDistMarkToContent() - capacity)] & 0xFF;
230 | Log.w(TAG2, "长度index跨界: " + (pos + adapter.getDistMarkToContent() - capacity));
231 | } else {
232 | contentLen = buffer[pos + adapter.getDistMarkToContent()] & 0xFF;
233 | }
234 | // Log.v(TAG2,"包len: " + contentLen);
235 | if (/*contentLen < 0 || */contentLen > 0x20) { // 内容长度为负数,或太大,都是辣鸡数据
236 | Log.w("buff", "内容长度异常: " + contentLen);
237 | printfBuffer();
238 | buffer[pos] = 0;// 把标志位抹掉
239 | pos++;
240 | continue;
241 | }
242 | // 判断后面的数据,是否足够一包
243 | if (getDistCurrPosToEnd(pos) > adapter.getLenPackageHeadToTail(contentLen)) { // 包长度(此处不包含包标志):包内容的长度+ 命令字(1字节) + 长度(1字节)
244 | packageLen = adapter.getLenOnePackage(contentLen)+ i; //(+1 表示加上包标志) i 表示垃圾数据的个数,检索到包标志前,有其他垃圾数据
245 | // Log.v(TAG2,"packageLen: " + packageLen);
246 | onePackage = pickOutOnePackageData(packageLen, readPos, mark);
247 | if (true) {
248 | // if(checkPackage(onePackage, contentLen) == 0) {
249 | // 校验通过,读取这包数据,放到包队列
250 | packageQueue.add(readArray(packageLen, mark));
251 | pos = readPos;
252 | cnt = readableSize;
253 | i = -1; // 马上执行一次++
254 | number++;
255 | continue;
256 | } else {
257 | // 错误的包数据
258 | Log.w("buff", "这包数据为辣鸡数据: " + BleLibByteUtil.BytesToHexStringPrintf(onePackage));
259 | buffer[pos] = 0;// 把标志位抹掉
260 | }
261 | } else {
262 | Log.w(TAG2, "不是完整包 后面的数据,不够一包: " + getDistCurrPosToEnd(pos));
263 | return -3;
264 | }
265 | } else {
266 | Log.w(TAG2, "不是完整包 后面没有包长度: " + getDistCurrPosToEnd(pos));
267 | return -2;
268 | }
269 | }
270 | pos++;
271 | }
272 | // Log.w(TAG2,"number == " + number);
273 | if (number == 0) {
274 | Log.w(TAG2, "没有找到包标志");
275 | return -1;
276 | }
277 | return 0;
278 | }
279 |
280 |
281 |
282 | /**
283 | * 从缓冲区复制一包数据,主要用来检测这包数据是否有效
284 | *
285 | * @param len
286 | * @param index
287 | * @return
288 | */
289 | private byte[] pickOutOnePackageData(int len, int index, byte mark) {
290 | if (IsEmpty() || len > capacity || len > readableSize) {
291 | Log.e("buff", "pickOut ReadArray error: " + len);
292 | return null;
293 | }
294 | // d("2 读之前的readableSize: " + readableSize + " readPos: " + readPos);
295 | byte[] temp = new byte[len];
296 | int pos = index;
297 | for (int i = 0; i < len; i++) {
298 | if (pos >= capacity) {
299 | pos = 0;
300 | }
301 | temp[i] = buffer[pos++];
302 | }
303 | // d("2 读后的readableSize: " + readableSize + " readPos: " + readPos);
304 | // Log.i("buff", "pickOut 找到包标志位 根据长度挑出一个包: " + BytesUtil.BytesToHexStringPrintf(temp));
305 | if (temp[0] != mark) {
306 | Log.w("buff", "pickOut 数据第一个字节不是正确的头: " + BleLibByteUtil.BytesToHexStringPrintf(temp));
307 | int j = 0;
308 | for (j = 0; j < len; j++) {
309 | if (temp[j] == mark) {
310 | break;
311 | }
312 | }
313 | // Log.i("buff", "pickOut size: " + temp.length + " len:" + len + " j: " + j);
314 | byte[] temp2 = Arrays.copyOfRange(temp, j, len); // 不包含最后一个字节,不用len-1
315 | Log.w("buff", "pickOut 截取后的数组:" + BleLibByteUtil.BytesToHexStringPrintf(temp2));
316 | return temp2;
317 | }
318 | return temp;
319 | }
320 |
321 | /**
322 | * 分析初始数据
323 | * 每个包只有一个标志位 0xAA 命令 长度 内容 校验
324 | * 读到标志位后,根据长度,把后面的读完
325 | *
326 | * @param data
327 | * @param length
328 | * @return
329 | */
330 | private int analyzeOriginalData(byte[] data, int length) {
331 | int ret = writeArray(data, length);
332 | if (ret == -1) {
333 | Log.w(TAG2, "writeArray 失败 缓冲器溢出 ");
334 | reset();
335 | return ret;
336 | }
337 | extractAllPackage(adapter.getPackageMark());
338 |
339 | if (packageQueue.size() == 0) {
340 | return -1;
341 | }
342 |
343 | mDataDispatchCenter.addAllPackageData(packageQueue, deviceId);
344 | return ret;
345 | }
346 |
347 | private long prevTime = 0;
348 | private long currTime = 0;
349 |
350 | public void pushOriginalDataToBuffer(byte[] originalData) {
351 | LogUtil.v(TAG2, "ble发来的原始数据: " + BleLibByteUtil.BytesToHexStringPrintf(originalData));
352 |
353 | // currTime = System.currentTimeMillis();
354 | // Log.v(TAG2, "push currTime: " + currTime);
355 | // Log.v(TAG2, "push 间隔: " + (currTime - prevTime));
356 | // prevTime = currTime;
357 | // 没有配置数据拼包适配器,则把原始数据分发出去
358 | if (adapter == null) {
359 | mDataDispatchCenter.dispatchOriginalPackage(originalData, deviceId);
360 | }
361 | else {
362 | // 此框架数据包拼包解析算法,数据包格式见:DataParserAdapter
363 | analyzeOriginalData(originalData, originalData.length);
364 | }
365 |
366 | }
367 |
368 | private void d(String msg) {
369 | Log.w("CircularBuffer", msg);
370 | }
371 | }
372 |
373 |
374 |
--------------------------------------------------------------------------------
/app/src/main/java/com/clock/blelib/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.clock.blelib;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.bluetooth.BluetoothAdapter;
6 | import android.bluetooth.BluetoothDevice;
7 | import android.bluetooth.BluetoothManager;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.pm.PackageManager;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.os.Handler;
14 | import android.support.annotation.Nullable;
15 | import android.support.v4.app.ActivityCompat;
16 | import android.support.v4.content.ContextCompat;
17 | import android.support.v7.widget.LinearLayoutManager;
18 | import android.support.v7.widget.RecyclerView;
19 | import android.util.Log;
20 | import android.view.Gravity;
21 | import android.view.View;
22 | import android.view.WindowManager;
23 | import android.widget.Button;
24 | import android.widget.PopupWindow;
25 | import android.widget.Toast;
26 |
27 | import com.clock.blelib.adapter.DeviceListAdapter;
28 | import com.clock.blelib.bleissus.blemodel.BLEManager;
29 | import com.clock.blelib.event.AddNewDeviceEvent;
30 | import com.clock.blelib.event.AddPreConnDeviceEvent;
31 | import com.clock.blelib.event.AddScanDeviceEvent;
32 | import com.clock.blelib.event.BleSendResultEvent;
33 | import com.clock.blelib.event.ConnectDeviceEvent;
34 | import com.clock.blelib.event.updateDeviceInfoEvent;
35 | import com.clock.blelib.widget.ScanListPopWindow;
36 | import com.clock.bluetoothlib.logic.network.connection.BLEAppDevice;
37 | import com.clock.bluetoothlib.logic.network.connection.BLEState;
38 | import com.clock.bluetoothlib.logic.utils.LogUtil;
39 |
40 | import org.greenrobot.eventbus.EventBus;
41 | import org.greenrobot.eventbus.Subscribe;
42 | import org.greenrobot.eventbus.ThreadMode;
43 |
44 | import java.util.ArrayList;
45 | import java.util.List;
46 |
47 | public class MainActivity extends BaseActivity implements View.OnClickListener {
48 |
49 | private static final int REQUEST_CODE = 10;
50 | private final int REQUEST_ENABLE_BT = 10086;
51 | private final String TAG = "MainActivity";
52 |
53 | public Handler mHandler = new Handler();
54 | public Handler mHandlerList = new Handler();
55 | private Activity mContext;
56 |
57 | private RecyclerView.LayoutManager mLayoutManager;
58 | private RecyclerView mRecyclerView;
59 | private DeviceListAdapter mDeviceListAdapter;
60 |
61 | private ScanListPopWindow scanListPopWindow;
62 | private Button btn_scan;
63 |
64 | private List mBleScanList;
65 | private List mBleDeviceList;
66 |
67 |
68 | @Override
69 | protected void onCreate(Bundle savedInstanceState) {
70 | super.onCreate(savedInstanceState);
71 | setContentView(R.layout.activity_main);
72 | Log.w(TAG, "main onCreate");
73 |
74 | initData();
75 | setAdapter();
76 | initView();
77 | verifyPermissions(this);
78 |
79 | if(!EventBus.getDefault().isRegistered(this)) {
80 | EventBus.getDefault().register(this);
81 | }
82 | }
83 | private void initData() {
84 | mContext = this;
85 | mBleScanList = new ArrayList<>();
86 | mBleDeviceList = new ArrayList<>();
87 | }
88 |
89 | private void setAdapter() {
90 | mRecyclerView = findViewById(R.id.rcview_device_list);
91 | mLayoutManager = new LinearLayoutManager(this);
92 | mRecyclerView.setLayoutManager(mLayoutManager);
93 | mDeviceListAdapter = new DeviceListAdapter(this, mBleDeviceList);
94 | mRecyclerView.setAdapter(mDeviceListAdapter);
95 | }
96 |
97 | private void initView() {
98 | btn_scan = findViewById(R.id.btn_scan);
99 | btn_scan.setOnClickListener(this);
100 |
101 | scanListPopWindow = new ScanListPopWindow(getApplicationContext(), this);
102 | }
103 |
104 | @Override
105 | public void onClick(View v) {
106 | switch (v.getId()) {
107 |
108 | case R.id.btn_scan:
109 | Log.d(TAG, "onClick: ");
110 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
111 | if (!bluetoothAdapter.isEnabled()) {
112 | Toast.makeText(this, getString(R.string.please_open_blue), Toast.LENGTH_LONG).show();
113 | return;
114 | }
115 |
116 | if (mBleDeviceList.size() >= 7) {
117 | Toast.makeText(this, getString(R.string.max_num), Toast.LENGTH_LONG).show();
118 | return;
119 | }
120 |
121 | mBleScanList.clear();
122 | BLEManager.getInstance().scanBLEDeviceManual();
123 |
124 | mHandler.removeCallbacksAndMessages(null);
125 | mHandler.postDelayed(new Runnable() {
126 | @Override
127 | public void run() {
128 | Log.d(TAG, "扫描结束10s");
129 | if (mBleScanList.size() <= 0/* && mBleDeviceList.size()<=0*/) {
130 |
131 | scanListPopWindow.dismiss();
132 | }
133 | else {
134 | showProgressBase(false);
135 | // scanListPopWindow.dismiss();
136 | }
137 | }
138 | }, 10000);
139 |
140 | showProgressBase(true);
141 | showScanListPopView();
142 | break;
143 |
144 | default:
145 | break;
146 | }
147 | }
148 |
149 | private void showScanListPopView() {
150 | scanListPopWindow.dismiss();
151 | scanListPopWindow.setFocusable(true);
152 | scanListPopWindow.selectItem = -1;
153 | scanListPopWindow.showAtLocation(MainActivity.this.findViewById(R.id.main_root),
154 | Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0);
155 | bgAlpha((float) 0.618);
156 | scanListPopWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
157 | @Override
158 | public void onDismiss() {
159 | bgAlpha((float) 1);
160 | if (scanListPopWindow.selectItem > -1) {
161 | showProgressBase(true);
162 | BluetoothDevice entry = mBleScanList.get(scanListPopWindow.selectItem);
163 | BLEManager.getInstance().connScannedBleDevice(entry);
164 | }
165 |
166 | mHandler.removeCallbacksAndMessages(null);
167 | mBleScanList.clear();
168 | scanListPopWindow.updateScanList(mBleScanList); // pop windows 只是隐藏,列表内容还在
169 | }
170 | });
171 | }
172 |
173 | private void bgAlpha(float f){
174 | WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
175 | layoutParams.alpha = f;
176 | getWindow().setAttributes(layoutParams);
177 | }
178 |
179 |
180 | private void preInit() {
181 | BluetoothManager bluetoothManager = (BluetoothManager) MainActivity.this.getSystemService(Context.BLUETOOTH_SERVICE);
182 | BluetoothAdapter bluetoothAdapter = null;
183 | if (bluetoothManager != null) {
184 | bluetoothAdapter = bluetoothManager.getAdapter();
185 | }
186 | else {
187 | Log.w(TAG, "init: bluetoothManager null");
188 | exitApp();
189 | }
190 |
191 | if (bluetoothAdapter != null) {
192 | if (!bluetoothAdapter.isEnabled()) {
193 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
194 | startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
195 | }
196 | else {
197 | Log.w(TAG, "start initBle ...");
198 | BLEManager.getInstance().initBle(getApplicationContext());
199 | }
200 | }
201 | }
202 |
203 |
204 | private void verifyPermissions(Activity activity) {
205 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
206 | List permissionList = new ArrayList<>();
207 | //检测是否有写的权限
208 | int permission = ActivityCompat.checkSelfPermission(activity,
209 | Manifest.permission.WRITE_EXTERNAL_STORAGE);
210 | if (permission != PackageManager.PERMISSION_GRANTED) {
211 | permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
212 | permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
213 | }
214 | //检测是否蓝牙的权限
215 | permission = ContextCompat.checkSelfPermission(activity,
216 | Manifest.permission.ACCESS_COARSE_LOCATION);
217 | if (permission != PackageManager.PERMISSION_GRANTED) {
218 | permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
219 | }
220 | if (permissionList.size() == 0) {
221 | preInit();
222 | return;
223 | }
224 | String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转为数组
225 | Log.d("test", "权限请求数组len :" + permissions.length);
226 | ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE);
227 | } else {
228 | preInit();
229 | }
230 | }
231 |
232 | @Override
233 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
234 | Log.d("test", "权限应答数组len :" + permissions.length + " grantResults len :" + grantResults.length);
235 | //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1
236 | if (requestCode == REQUEST_CODE) {
237 | Log.d("test", "权限请求反馈 REQUEST_CODE");
238 | if (grantResults.length <= 0) { // 用户不给权限,用不了app
239 | Log.d("test", "用户不给权限,用不了app ");
240 | exitApp();
241 | return;
242 | }
243 | for (int i = 0; i < grantResults.length; i++) {
244 | Log.d("test", "i: " + i + " permissions: " + permissions[i ] + " grantResults: " + grantResults[i]);
245 | if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
246 | Log.d("test", "动态权限没打开: " + i);
247 | exitApp();
248 | return;
249 | }
250 | }
251 | preInit();
252 | } else {
253 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
254 | }
255 | }
256 |
257 | @Override
258 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
259 | super.onActivityResult(requestCode, resultCode, data);
260 | Log.i(TAG, "requestCode: " + requestCode + " resultCode: " + resultCode);
261 | if (requestCode == REQUEST_ENABLE_BT) {
262 | if (resultCode == RESULT_OK) {
263 | Log.d(TAG, "打开蓝牙成功!");
264 | Log.w(TAG, "start initBle ...");
265 | BLEManager.getInstance().initBle(getApplicationContext());
266 | }
267 | else if (resultCode == RESULT_CANCELED) {
268 | Log.d(TAG, "放弃打开蓝牙!");
269 | exitApp();
270 | }
271 | }
272 | }
273 |
274 | private void exitApp() {
275 | finish();
276 | android.os.Process.killProcess(android.os.Process.myPid()); // 获取PID
277 | System.exit(0);
278 | }
279 |
280 |
281 | @Subscribe(threadMode = ThreadMode.MAIN)
282 | public void onMessageEvent(AddScanDeviceEvent event) {
283 | Log.d(TAG, "onMessageEvent: " + event.toString());
284 | for (BLEAppDevice d: mBleDeviceList) {
285 | if (d.mBleAddress.equals(event.getBluetoothDevice().getAddress())) {
286 | return;
287 | }
288 | }
289 | mBleScanList.add(event.getBluetoothDevice());
290 | scanListPopWindow.updateScanList(mBleScanList);
291 | }
292 |
293 | @Subscribe(threadMode = ThreadMode.MAIN)
294 | public void onMessageEvent(AddPreConnDeviceEvent event) {
295 |
296 | }
297 |
298 | @Subscribe(threadMode = ThreadMode.MAIN)
299 | public void onMessageEvent(AddNewDeviceEvent event) {
300 | Log.d(TAG, "AddDeviceListEven: " + event.toString());
301 | for (BLEAppDevice d : mBleDeviceList) {
302 | if (d.mDeviceId == event.device.mDeviceId) {
303 | Log.w(TAG, "设备添加重复:" + d.mBleAddress + " " + d.mBleName);
304 | return;
305 | }
306 | }
307 |
308 | // 新连接的设备,则添加进去
309 | if (!mBleDeviceList.contains(event.device)) {
310 | mBleDeviceList.add(event.device);
311 | LogUtil.i(TAG, "新连接的设备,添加到列表");
312 | }
313 |
314 | if (!BLEManager.getInstance().hasDeviceConnecting()) {
315 | showProgressBase(false);
316 | }
317 |
318 | mDeviceListAdapter.update();
319 |
320 | }
321 |
322 | @Subscribe(threadMode = ThreadMode.MAIN)
323 | public void onMessageEvent(updateDeviceInfoEvent event) {
324 | Log.d(TAG, "onMessageEvent updateDeviceListEven: ");
325 | mDeviceListAdapter.update();
326 | showProgressBase(false);
327 | }
328 |
329 | @Subscribe(threadMode = ThreadMode.MAIN)
330 | public void onMessageEvent(ConnectDeviceEvent event) {
331 | Log.d(TAG, "onMessageEvent ConnectDeviceEven: ");
332 | if (event.type == 0) { // 连接失败 0
333 | Log.d(TAG, "ReconnectType " + event.device.mReconnectType);
334 | if (event.device.mReconnectType == BLEState.ReconnectType.Manual) {
335 |
336 | } else { // 当前设备自动重连 失败
337 |
338 | }
339 |
340 | showProgressBase(false);
341 | }
342 | }
343 |
344 | @Subscribe(threadMode = ThreadMode.MAIN)
345 | public void onMessageEvent(BleSendResultEvent event) {
346 | Log.w(TAG, "BleSendResultEven : " + event.result);
347 | showProgressBase(false);
348 | }
349 |
350 | }
351 |
--------------------------------------------------------------------------------