├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── jarRepositories.xml
├── kotlinc.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── LICENSE.txt
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── davistsin
│ │ └── bluetoothlelibrary
│ │ ├── MainActivity.java
│ │ ├── MainViewModel.java
│ │ └── util
│ │ └── LocationUtil.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── document
├── version-0.1.0.md
├── version-0.1.1.md
├── version-0.2.0.md
├── version-0.3.0.md
└── version-0.5.6.md
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── image
└── title.jpg
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── davistsin
│ │ └── bluetoothle
│ │ ├── BleHelper.java
│ │ ├── main
│ │ ├── connect
│ │ │ ├── BleConnectCreator.java
│ │ │ ├── BleConnector.java
│ │ │ ├── BluetoothUtil.java
│ │ │ ├── ConnParameters.java
│ │ │ ├── ConnectorSettings.java
│ │ │ └── Request.java
│ │ └── listener
│ │ │ ├── BleConnectionListener.java
│ │ │ ├── BleDiscoverServicesListener.java
│ │ │ ├── BleIndicationListener.java
│ │ │ ├── BleNotificationListener.java
│ │ │ ├── BleReadCharacteristicListener.java
│ │ │ ├── BleRssiListener.java
│ │ │ └── BleWriteCharacteristicListener.java
│ │ └── replicas
│ │ ├── BleGattServer.java
│ │ ├── IServerListener.java
│ │ ├── OnAdvertiseListener.java
│ │ ├── OnConnectionStateChangeListener.java
│ │ ├── OnReadRequestListener.java
│ │ ├── OnServiceAddedListener.java
│ │ ├── OnWriteRequestListener.java
│ │ ├── ServiceProfile.java
│ │ └── ServiceSettings.java
│ └── res
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Davis Tsin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://jitpack.io/#davistsin/BluetoothLELibrary) 
4 |
5 | [固件升级/硬件升级/DFU](https://github.com/qindachang/DFUDemo "固件升级/硬件升级/DFU")
6 |
7 | 低功耗蓝牙库。**优势**:
8 |
9 | 1. **具有**主机、从机模式。在从机模式中,本机也可以开启蓝牙服务、读写特征。
10 | 2. 同时连接多台蓝牙设备。
11 | 3. 适配小米手机连接蓝牙操作。
12 | 4. 适配三星手机发现服务、开启通知等。
13 | 5. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
14 | 6. 支持同时**开启多个通知**。
15 | 7. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
16 | 8. 队列定时设置,满足因公司需求蓝牙时间间隔。
17 | 9. 设备信号强度、距离计算回调,可用于防丢器产品。
18 |
19 | ### 注意点:
20 |
21 | 1. Android 6.0 扫描蓝牙需要地理位置权限。[easypermissions](https://github.com/googlesamples/easypermissions "easypermissions")
22 | 2. Android 7.0 扫描蓝牙需要地理位置权限,并且需要开启系统位置信息。
23 | [LocationUtils](https://github.com/qindachang/BluetoothLELibrary/blob/master/app/src/main/java/com/qindachang/bluetoothlelibrary/LocationUtils.java "LocationUtils")
24 | 3. Android 12 需要声明 BLUETOOTH_SCAN、BLUETOOTH_CONNECT 权限;如果使用蓝牙从机,则需要 BLUETOOTH_ADVERTISE 权限。(targets API 大于等于31时)
25 | 4. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
26 | 5. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
27 |
28 | ## 开始
29 |
30 | ### 集成
31 |
32 | 添加依赖
33 |
34 | ```
35 |
36 | allprojects {
37 | repositories {
38 | ...
39 | maven { url 'https://jitpack.io' }
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation 'com.github.davistsin:BluetoothLELibrary:{latest version}'
45 | }
46 |
47 | ```
48 |
49 | 蓝牙扫描推荐使用 [Android-Scanner-Compat-Library](https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library)
50 |
51 | ```
52 | implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
53 | ```
54 |
55 | **权限:**
56 |
57 | ```
58 |
59 |
60 | ```
61 |
62 | 5.0以上需要
63 |
64 | ```
65 |
66 |
67 | ```
68 |
69 | 6.0以上需要
70 |
71 | ```
72 |
73 |
74 | ```
75 |
76 | 12以上需要
77 |
78 | ```
79 |
80 |
81 |
82 |
83 |
84 | ```
85 |
86 | ---
87 |
88 | ### 一、 使用介绍(主机模式)
89 |
90 | 是否支持蓝牙
91 |
92 | ```Java
93 | BleHelper.isSupportBluetooth();
94 | ```
95 |
96 | 蓝牙是否打开
97 |
98 | ```Java
99 | BleHelper.isBluetoothEnable();
100 | ```
101 |
102 | 打开蓝牙
103 |
104 | ```Java
105 | BleHelper.enableBluetooth();
106 | ```
107 |
108 | 关闭蓝牙
109 |
110 | ```Java
111 | BleHelper.disableBluetooth();
112 | ```
113 |
114 | 获取已连接的蓝牙设备列表
115 |
116 | ```Java
117 | BleHelper.getConnectedDevices(this);
118 | ```
119 |
120 | #### 1. 扫描
121 |
122 | 文档前往:
123 |
124 | [https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library](https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library)
125 |
126 | #### 2. 连接
127 |
128 | 可以创建多个连接实例
129 |
130 | ```java
131 | ConnectorSettings settings = new ConnectorSettings.Builder()
132 | .autoConnect(true)
133 | .autoDiscoverServices(true)
134 | .enableQueue(true)
135 | .setQueueIntervalTime(ConnectorSettings.QUEUE_INTERVAL_TIME_AUTO)
136 | .build();
137 | BleConnector bleConnector = BleConnectCreator.create(MainActivity.this, bluetoothDevice, settings);
138 |
139 | bleConnector.connect();
140 |
141 | bleConnector.addConnectionListener(new BleConnectionListener() {
142 | @Override
143 | public void onConnecting() {
144 |
145 | }
146 |
147 | @Override
148 | public void onConnected(BluetoothDevice device) {
149 |
150 | }
151 |
152 | @Override
153 | public void onDisconnected() {
154 |
155 | }
156 |
157 | @Override
158 | public void onFailure() {
159 |
160 | }
161 | });
162 | ```
163 | #### 3. 发现服务
164 |
165 | ```java
166 | bleConnector.discoverServices();
167 |
168 | bleConnector.addDiscoveryListener(new BleDiscoverServicesListener() {
169 | @Override
170 | public void onServicesDiscovered(BluetoothGatt gatt) {
171 |
172 | }
173 |
174 | @Override
175 | public void onFailure() {
176 |
177 | }
178 | });
179 | ```
180 | #### 4. 读取数据
181 |
182 | ```java
183 | bleConnector.readCharacteristic("serviceUUID", "characteristicUUID");
184 |
185 | bleConnector.addReadCharacteristicListener(new BleReadCharacteristicListener() {
186 | @Override
187 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
188 | byte[] data = characteristic.getValue();
189 | }
190 |
191 | @Override
192 | public void onFailure() {
193 |
194 | }
195 | });
196 | ```
197 | #### 5. 发送数据
198 | ```java
199 | bleConnector.writeCharacteristic(new byte[]{0x01,0x02,0x03}, "serviceUUID", "characteristicUUID");
200 |
201 | bleConnector.addWriteCharacteristicListener(new BleWriteCharacteristicListener() {
202 | @Override
203 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
204 |
205 | }
206 |
207 | @Override
208 | public void onFailed() {
209 |
210 | }
211 | });
212 | ```
213 | #### 6. 通知
214 | ```java
215 | // 打开Notification
216 | bleConnector.enableNotification(true, "serviceUUID", "characteristicUUID");
217 | // 关闭Notification
218 | bleConnector.enableNotification(false, "serviceUUID", "characteristicUUID");
219 |
220 | bleConnector.addNotificationListener(new BleNotificationListener() {
221 | @Override
222 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
223 | byte[] data = characteristic.getValue();
224 | }
225 |
226 | @Override
227 | public void onFailed() {
228 |
229 | }
230 | });
231 |
232 | // 打开Indication
233 | bleConnector.enableIndication(true, "serviceUUID", "characteristicUUID");
234 | // 关闭Indication
235 | bleConnector.enableIndication(false, "serviceUUID", "characteristicUUID");
236 |
237 | bleConnector.addIndicationListener(new BleIndicationListener() {
238 | @Override
239 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
240 | byte[] data = characteristic.getValue();
241 | }
242 |
243 | @Override
244 | public void onFailed() {
245 |
246 | }
247 | });
248 | ```
249 | #### 7. 移除监听、关闭连接
250 | ```java
251 | bleConnector.removeAllListeners();
252 |
253 | bleConnector.close();
254 | ```
255 |
256 | ---
257 |
258 | ### 二、使用介绍(从机模式)
259 |
260 | #### 1. 启动
261 |
262 | ```java
263 | private BleGattServer mGattServer = new BleGattServer();
264 |
265 | mGattServer.startAdvertising(UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb")); // 该uuid可提供给主机client过滤扫描,可以自定义
266 | mGattServer.startServer(context);
267 | ```
268 | #### 2. 开始广播/停止广播
269 |
270 | ```java
271 | mGattServer.startAdvertising("serviceUUID");
272 |
273 | mGattServer.stopAdvertising();
274 | ```
275 |
276 | #### 4. 添加蓝牙服务Service
277 |
278 | ```java
279 | List list = new ArrayList<>();
280 |
281 | // 设置一个写的特征
282 | ServiceProfile profile = new ServiceProfile();
283 | profile.setCharacteristicUuid(UUID.fromString("0000fff3-0000-1000-8000-00805f9b34fb"));
284 | profile.setCharacteristicProperties(BluetoothGattCharacteristic.PROPERTY_WRITE);
285 | profile.setCharacteristicPermission(BluetoothGattCharacteristic.PERMISSION_WRITE);
286 | profile.setDescriptorUuid(GattServer.CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
287 | profile.setDescriptorPermission(BluetoothGattDescriptor.PERMISSION_READ);
288 | profile.setDescriptorValue(new byte[]{0});
289 | list.add(profile);
290 |
291 | // 设置一个读的特征
292 | ServiceProfile profile1 = new ServiceProfile();
293 | profile1.setCharacteristicUuid(UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb"));
294 | profile1.setCharacteristicProperties(BluetoothGattCharacteristic.PROPERTY_READ);
295 | profile1.setCharacteristicPermission(BluetoothGattCharacteristic.PERMISSION_READ);
296 | profile1.setDescriptorUuid(GattServer.CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
297 | profile1.setDescriptorPermission(BluetoothGattDescriptor.PERMISSION_READ);
298 | profile1.setDescriptorValue(new byte[]{1});
299 | list.add(profile1);
300 |
301 | // 设置一个notify通知
302 | ServiceProfile profile2 = new ServiceProfile();
303 | profile2.setCharacteristicUuid(UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb"));
304 | profile2.setCharacteristicProperties(BluetoothGattCharacteristic.PROPERTY_NOTIFY);
305 | profile2.setCharacteristicPermission(BluetoothGattCharacteristic.PERMISSION_READ);
306 | profile2.setDescriptorUuid(GattServer.CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
307 | profile2.setDescriptorPermission(BluetoothGattDescriptor.PERMISSION_WRITE);
308 | profile2.setDescriptorValue(new byte[]{1});
309 | list.add(profile2);
310 |
311 | final ServiceSettings serviceSettings = new ServiceSettings.Builder()
312 | .setServiceUuid(UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb"))//服务uuid
313 | .setServiceType(BluetoothGattService.SERVICE_TYPE_PRIMARY)
314 | .addServiceProfiles(list)//上述设置添加到该服务里
315 | .build();
316 |
317 | mGattServer.addService(serviceSettings);
318 | ```
319 |
320 | #### 4. 回调监听
321 |
322 | ```java
323 | mGattServer.addOnAdvertiseListener(new OnAdvertiseListener() {
324 | @Override
325 | public void onStartSuccess(AdvertiseSettings settingsInEffect) {
326 | setContentText("开启广播 成功,uuid:0000fff0-0000-1000-8000-00805f9b34fb");
327 | }
328 |
329 | @Override
330 | public void onStartFailure(int errorCode) {
331 | setContentText("开启广播 失败,uuid:0000fff0-0000-1000-8000-00805f9b34fb");
332 | }
333 |
334 | @Override
335 | public void onStopAdvertising() {
336 | setContentText("停止广播,uuid:0000fff0-0000-1000-8000-00805f9b34fb");
337 | }
338 | });
339 |
340 | mGattServer.addOnServiceAddedListener(new OnServiceAddedListener() {
341 | @Override
342 | public void onSuccess(BluetoothGattService service) {
343 | setContentText("添加服务成功!");
344 | }
345 |
346 | @Override
347 | public void onFail(BluetoothGattService service) {
348 | setContentText("添加服务失败");
349 | }
350 | });
351 |
352 | mGattServer.addOnConnectionStateChangeListener(new OnConnectionStateChangeListener() {
353 | @Override
354 | public void onChange(BluetoothDevice device, int status, int newState) {
355 |
356 | }
357 |
358 | @Override
359 | public void onConnected(BluetoothDevice device) {
360 | setContentText("连接上一台设备 :{ name = " + device.getName() + ", address = " + device.getAddress() + "}");
361 | mBluetoothDevice = device;
362 | }
363 |
364 | @Override
365 | public void onDisconnected(BluetoothDevice device) {
366 | setContentText("设备断开连接 :{ name = " + device.getName() + ", address = " + device.getAddress() + "}");
367 | }
368 | });
369 |
370 | mGattServer.addOnWriteRequestListener(new OnWriteRequestListener() {
371 | @Override
372 | public void onCharacteristicWritten(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) {
373 | setContentText("设备写入特征请求 : device = " + device.getAddress() + ", characteristic uuid = " + characteristic.getUuid().toString() + ", value = " + Arrays.toString(value));
374 | }
375 |
376 | @Override
377 | public void onDescriptorWritten(BluetoothDevice device, BluetoothGattDescriptor descriptor, byte[] value) {
378 | setContentText("设备写入描述请求 : device = " + device.getAddress() + ", descriptor uuid = " + descriptor.getUuid().toString() + ", value = " + Arrays.toString(value));
379 | }
380 | });
381 |
382 | ```
383 |
384 | #### 5. 移除监听、关闭
385 |
386 | ```java
387 | mGattServer.removeAllListeners();
388 |
389 | mGattServer.closeServer();
390 | ```
391 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 | compileOptions {
7 | sourceCompatibility JavaVersion.VERSION_1_8
8 | targetCompatibility JavaVersion.VERSION_1_8
9 | }
10 |
11 | defaultConfig {
12 | applicationId "com.davistsin.bluetoothlelibrary"
13 | minSdkVersion rootProject.ext.minSdkVersion
14 | targetSdkVersion rootProject.ext.targetSdkVersion
15 | versionCode 1
16 | versionName "1.0"
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(include: ['*.jar'], dir: 'libs')
28 | implementation project(':library')
29 | implementation 'androidx.appcompat:appcompat:1.2.0'
30 | implementation 'com.google.android.material:material:1.3.0'
31 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
32 |
33 | implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
34 | }
35 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/davistsin/bluetoothlelibrary/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothlelibrary;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import android.bluetooth.BluetoothDevice;
7 | import android.bluetooth.BluetoothGatt;
8 | import android.bluetooth.BluetoothGattCharacteristic;
9 | import android.os.Bundle;
10 |
11 | import com.davistsin.bluetoothle.main.connect.BleConnectCreator;
12 | import com.davistsin.bluetoothle.main.connect.BleConnector;
13 | import com.davistsin.bluetoothle.main.connect.ConnectorSettings;
14 | import com.davistsin.bluetoothle.main.listener.BleConnectionListener;
15 | import com.davistsin.bluetoothle.main.listener.BleDiscoverServicesListener;
16 | import com.davistsin.bluetoothle.main.listener.BleIndicationListener;
17 | import com.davistsin.bluetoothle.main.listener.BleNotificationListener;
18 | import com.davistsin.bluetoothle.main.listener.BleReadCharacteristicListener;
19 | import com.davistsin.bluetoothle.main.listener.BleWriteCharacteristicListener;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
25 | import no.nordicsemi.android.support.v18.scanner.ScanCallback;
26 | import no.nordicsemi.android.support.v18.scanner.ScanFilter;
27 | import no.nordicsemi.android.support.v18.scanner.ScanResult;
28 | import no.nordicsemi.android.support.v18.scanner.ScanSettings;
29 |
30 | public class MainActivity extends AppCompatActivity {
31 | private String mUuid = "";
32 | private BluetoothLeScannerCompat mScannerCompat;
33 |
34 | private final ScanCallback mScanCallback = new ScanCallback() {
35 | @Override
36 | public void onScanResult(int callbackType, @NonNull ScanResult result) {
37 | super.onScanResult(callbackType, result);
38 | BluetoothDevice bluetoothDevice = result.getDevice();
39 | }
40 |
41 | @Override
42 | public void onBatchScanResults(@NonNull List results) {
43 | super.onBatchScanResults(results);
44 | }
45 |
46 | @Override
47 | public void onScanFailed(int errorCode) {
48 | super.onScanFailed(errorCode);
49 | }
50 | };
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | setContentView(R.layout.activity_main);
56 |
57 | mScannerCompat = BluetoothLeScannerCompat.getScanner();
58 | ScanSettings settings = new ScanSettings.Builder()
59 | .setLegacy(false)
60 | .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
61 | .setReportDelay(5000)
62 | .setUseHardwareBatchingIfSupported(true)
63 | .build();
64 | List filters = new ArrayList<>();
65 | filters.add(new ScanFilter.Builder().build());
66 | mScannerCompat.startScan(filters, settings, mScanCallback);
67 | }
68 |
69 | @Override
70 | protected void onDestroy() {
71 | super.onDestroy();
72 | mScannerCompat.stopScan(mScanCallback);
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/davistsin/bluetoothlelibrary/MainViewModel.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothlelibrary;
2 |
3 | import android.app.Application;
4 | import android.bluetooth.BluetoothDevice;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.lifecycle.AndroidViewModel;
8 | import androidx.lifecycle.ViewModel;
9 |
10 | import com.davistsin.bluetoothle.main.connect.BleConnectCreator;
11 | import com.davistsin.bluetoothle.main.connect.BleConnector;
12 | import com.davistsin.bluetoothle.main.connect.ConnectorSettings;
13 |
14 | public class MainViewModel extends AndroidViewModel {
15 | public MainViewModel(@NonNull Application application) {
16 | super(application);
17 | }
18 |
19 | public void connectBluetooth(BluetoothDevice device) {
20 | ConnectorSettings connectorSettings = new ConnectorSettings.Builder()
21 | .autoConnect(true)
22 | .autoDiscoverServices(true)
23 | .enableQueue(true)
24 | .setQueueIntervalTime(ConnectorSettings.QUEUE_INTERVAL_TIME_AUTO)
25 | .build();
26 | BleConnector connector = BleConnectCreator.create(getApplication(), device, connectorSettings);
27 | connector.connect();
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/davistsin/bluetoothlelibrary/util/LocationUtil.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothlelibrary.util;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.location.LocationManager;
7 |
8 |
9 | public class LocationUtil {
10 |
11 | private LocationUtil() {
12 | //no instance
13 | }
14 |
15 | /**
16 | * 判断是否启动定位服务
17 | *
18 | * @param context context
19 | * @return 是否启动定位服务
20 | */
21 | public static boolean isOpenLocService(final Context context) {
22 | boolean isGps = false; //判断GPS定位是否启动
23 | boolean isNetwork = false; //判断网络定位是否启动
24 | if (context != null) {
25 | LocationManager locationManager
26 | = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
27 | if (locationManager != null) {
28 | //通过GPS卫星定位,定位级别可以精确到街(通过24颗卫星定位,在室外和空旷的地方定位准确、速度快)
29 | isGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
30 | //通过WLAN或移动网络(3G/2G)确定的位置(也称作AGPS,辅助GPS定位。主要用于在室内或遮盖物(建筑群或茂密的深林等)密集的地方定位)
31 | isNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
32 | }
33 | if (isGps || isNetwork) {
34 | return true;
35 | }
36 | }
37 | return false;
38 | }
39 |
40 | /**
41 | * 跳转定位服务界面
42 | *
43 | * @param activity activity
44 | */
45 | public static void gotoLocServiceSettings(Activity activity) {
46 | final Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
47 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
48 | activity.startActivity(intent);
49 | }
50 |
51 | /**
52 | * 跳转定位服务界面
53 | *
54 | * @param activity activity
55 | * @param requestCode requestCode
56 | */
57 | public static void gotoLocServiceSettings(Activity activity, int requestCode) {
58 | final Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
59 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
60 | activity.startActivityForResult(intent, requestCode);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BluetoothLE Library
3 |
4 |
5 | Hello blank fragment
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "29.0.2"
6 | minSdkVersion = 18
7 | compileSdkVersion = 29
8 | targetSdkVersion = 29
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath "com.android.tools.build:gradle:4.1.3"
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | mavenCentral()
23 | maven { url 'https://jitpack.io' }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/document/version-0.1.0.md:
--------------------------------------------------------------------------------
1 | #BluetoothLE library
2 |
3 | [English](https://github.com/qindachang/BluetoothLELibrary/blob/master/README-EN.md "English")
4 |
5 | 低功耗蓝牙框架。**优势**:
6 |
7 | 1. **适配**到Android5.0和Android6.0的扫描方式(速度极快)。
8 | 2. 适配小米手机连接蓝牙操作。
9 | 3. 适配三星手机发现服务、开启通知等。
10 | 4. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
11 | 5. 支持同时**开启多个通知**。
12 | 6. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
13 | 6. 扫描操作支持-> 设置扫描时长、根据设备名称扫描、根据硬件地址扫描、根据服务UUID扫描、连接成功后自动关闭扫描。
14 |
15 |
16 | ###注意点:
17 | 1. Android 6.0扫描蓝牙需要地理位置权限。
18 | 2. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
19 | 3. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
20 |
21 | ##入门指南
22 |
23 | 权限:
24 |
25 |
26 |
27 |
28 |
29 | 6.0以上设备需要
30 |
31 |
32 |
33 |
34 | **引入方式**
35 |
36 | compile 'com.qindachang:BluetoothLELibrary:0.1.0'
37 |
38 |
39 | **一、获取单例实例**
40 |
41 | BluetoothLe mBluetoothLe = BluetoothLe.getDefault();
42 |
43 | **二、初始化**
44 |
45 | mBluetoothLe.init(this);//必须调用init()初始化
46 |
47 | **三、扫描**
48 |
49 | 扫描过程已携带6.0动态权限申请:地理位置权限
50 |
51 | private BluetoothDevice mBluetoothDevice;
52 |
53 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
54 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
55 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
56 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
57 | .startBleScan(this, new OnLeScanListener() {
58 | @Override
59 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
60 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
61 | tv_text.setText(mStringBuilder.toString());
62 | mBluetoothDevice = bluetoothDevice;
63 | }
64 |
65 | @Override
66 | public void onBatchScanResults(List results) {
67 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
68 | tv_text.setText(mStringBuilder.toString());
69 | }
70 |
71 | @Override
72 | public void onScanCompleted() {
73 | mStringBuilder.append("扫描结束\n");
74 | tv_text.setText(mStringBuilder.toString());
75 | }
76 |
77 | @Override
78 | public void onScanFailed(int status) {
79 | mStringBuilder.append("扫描错误\n");
80 | tv_text.setText(mStringBuilder.toString());
81 | }
82 | });
83 |
84 | **四、停止扫描**
85 |
86 | mBluetoothLe.stopBleScan();
87 |
88 | **五、连接蓝牙**
89 |
90 | //发送数据、开启通知等操作,必须等待onServicesDiscovered()发现服务回调后,才能去操作
91 | //参数:false为关闭蓝牙断开自动重连,如果为true则自动断开重连
92 | mBluetoothLe.startBleConnect(false, mBluetoothDevice, new OnLeConnectListener() {
93 |
94 | @Override
95 | public void onDeviceConnecting() {
96 | mStringBuilder.append("连接中...\n");
97 | tv_text.setText(mStringBuilder.toString());
98 | }
99 |
100 | @Override
101 | public void onDeviceConnected() {
102 | mStringBuilder.append("成功连接!\n");
103 | tv_text.setText(mStringBuilder.toString());
104 | }
105 |
106 |
107 | @Override
108 | public void onDeviceDisconnected() {
109 | mStringBuilder.append("断开连接!\n");
110 | tv_text.setText(mStringBuilder.toString());
111 | }
112 |
113 | @Override
114 | public void onServicesDiscovered(BluetoothGatt gatt) {
115 | mStringBuilder.append("发现服务啦\n");
116 | tv_text.setText(mStringBuilder.toString());
117 | }
118 |
119 | @Override
120 | public void onDeviceConnectFail() {
121 | mStringBuilder.append("连接失败~~\n");
122 | tv_text.setText(mStringBuilder.toString());
123 | }
124 | });
125 |
126 | **断开连接**
127 |
128 | mBluetoothLe.disconnect();
129 |
130 | **五、发送数据(到蓝牙特征)**
131 |
132 | //以下两个参数为硬件工程师提供,请你与你司的硬件工程师沟通
133 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
134 | private static final String WRITE_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
135 |
136 | mBluetoothLe.writeDataToCharacteristic(bytes, SERVICE_UUID, WRITE_UUID);
137 |
138 | **六、监听发送数据**
139 |
140 | mBluetoothLe.setOnWriteCharacteristicListener(new OnLeWriteCharacteristicListener() {
141 | @Override
142 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
143 | Log.d("debug", "发送了数据:" + Arrays.toString(characteristic.getValue()));
144 | mStringBuilder.append("发送了:").append(Arrays.toString(characteristic.getValue()));
145 | mStringBuilder.append("\n");
146 | tv_text.setText(mStringBuilder.toString());
147 | }
148 |
149 | @Override
150 | public void onFailed(String msg, int status) {
151 |
152 | }
153 | });
154 |
155 | **七、开启通知**
156 |
157 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
158 | private static final String HEART_NOTIFICATION_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
159 | private static final String STEP_NOTIFICATION_UUID = "0000fff3-0000-1000-8000-00805f9b34fb";
160 |
161 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
162 |
163 | **八、开启多个通知**
164 |
165 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
166 |
167 | **九、监听通知**
168 |
169 | mBluetoothLe.setBleNotificationListener(new OnLeNotificationListener() {
170 | @Override
171 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
172 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
173 | }
174 |
175 | @Override
176 | public void onFailure() {
177 |
178 | }
179 | });
180 |
181 | **十、读取数据**
182 |
183 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
184 | private static final String READ_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
185 |
186 | mBluetoothLe.readCharacteristic(SERVICE_UUID, READ_UUID);
187 |
188 | mBluetoothLe.setOnReadCharacteristicListener(new OnLeReadCharacteristicListener() {
189 | @Override
190 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
191 |
192 | }
193 |
194 | @Override
195 | public void onFailure(String info, int status) {
196 |
197 | }
198 | });
199 |
200 |
201 | **十一、关闭GATT**
202 |
203 | 在你退出应用的时候使用
204 |
205 | mBluetoothLe.close();
206 |
207 | ###避免内存泄露
208 |
209 | 在Activity生命周期onDestroy() 中使用:
210 |
211 | mBluetoothLe.destroy();
212 |
213 |
214 | ##仍在补充
215 | 1. 连续操作发送数据、读取特征、开启通知操作设置优先级,像网络请求一样设置优先级
216 | 2. 蓝牙设备信号强度监听
217 | 3. 清理蓝牙缓存
218 | 4. 连接超时设置,连接不上的情况下自动重连的次数设置
219 |
220 | ##了解更多
221 |
222 | 1. See Demo
223 | 2. QQ: 714275846 / 823951895
224 | 3. 邮箱:qindachang@outlook.com
225 | 4. 博客:http://blog.csdn.net/u013003052
226 | 5. Github: https://github.com/qindachang
--------------------------------------------------------------------------------
/document/version-0.1.1.md:
--------------------------------------------------------------------------------
1 |
2 | 新增功能:取消所有队列。
3 |
4 | #BluetoothLE library
5 |
6 | [English](https://github.com/qindachang/BluetoothLELibrary/blob/master/README-EN.md "English")
7 |
8 | 低功耗蓝牙框架。**优势**:
9 |
10 | 1. **适配**到Android5.0和Android6.0的扫描方式(速度极快)。
11 | 2. 适配小米手机连接蓝牙操作。
12 | 3. 适配三星手机发现服务、开启通知等。
13 | 4. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
14 | 5. 支持同时**开启多个通知**。
15 | 6. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
16 | 6. 扫描操作支持-> 设置扫描时长、根据设备名称扫描、根据硬件地址扫描、根据服务UUID扫描、连接成功后自动关闭扫描。
17 |
18 |
19 | ###注意点:
20 | 1. Android 6.0扫描蓝牙需要地理位置权限。
21 | 2. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
22 | 3. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
23 |
24 | ##入门指南
25 |
26 | 权限:
27 |
28 |
29 |
30 |
31 |
32 | 6.0以上设备需要
33 |
34 |
35 |
36 |
37 | **引入方式**
38 |
39 | compile 'com.qindachang:BluetoothLELibrary:0.1.1'
40 |
41 |
42 | **一、获取单例实例**
43 |
44 | BluetoothLe mBluetoothLe = BluetoothLe.getDefault();
45 |
46 | **二、初始化**
47 |
48 | mBluetoothLe.init(this);//必须调用init()初始化
49 |
50 | **三、扫描**
51 |
52 | 扫描过程已携带6.0动态权限申请:地理位置权限
53 |
54 | private BluetoothDevice mBluetoothDevice;
55 |
56 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
57 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
58 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
59 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
60 | .startBleScan(this, new OnLeScanListener() {
61 | @Override
62 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
63 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
64 | tv_text.setText(mStringBuilder.toString());
65 | mBluetoothDevice = bluetoothDevice;
66 | }
67 |
68 | @Override
69 | public void onBatchScanResults(List results) {
70 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
71 | tv_text.setText(mStringBuilder.toString());
72 | }
73 |
74 | @Override
75 | public void onScanCompleted() {
76 | mStringBuilder.append("扫描结束\n");
77 | tv_text.setText(mStringBuilder.toString());
78 | }
79 |
80 | @Override
81 | public void onScanFailed(int status) {
82 | mStringBuilder.append("扫描错误\n");
83 | tv_text.setText(mStringBuilder.toString());
84 | }
85 | });
86 |
87 | **四、停止扫描**
88 |
89 | mBluetoothLe.stopBleScan();
90 |
91 | **五、连接蓝牙**
92 |
93 | //发送数据、开启通知等操作,必须等待onServicesDiscovered()发现服务回调后,才能去操作
94 | //参数:false为关闭蓝牙断开自动重连,如果为true则自动断开重连
95 | mBluetoothLe.startBleConnect(false, mBluetoothDevice, new OnLeConnectListener() {
96 |
97 | @Override
98 | public void onDeviceConnecting() {
99 | mStringBuilder.append("连接中...\n");
100 | tv_text.setText(mStringBuilder.toString());
101 | }
102 |
103 | @Override
104 | public void onDeviceConnected() {
105 | mStringBuilder.append("成功连接!\n");
106 | tv_text.setText(mStringBuilder.toString());
107 | }
108 |
109 |
110 | @Override
111 | public void onDeviceDisconnected() {
112 | mStringBuilder.append("断开连接!\n");
113 | tv_text.setText(mStringBuilder.toString());
114 | }
115 |
116 | @Override
117 | public void onServicesDiscovered(BluetoothGatt gatt) {
118 | mStringBuilder.append("发现服务啦\n");
119 | tv_text.setText(mStringBuilder.toString());
120 | }
121 |
122 | @Override
123 | public void onDeviceConnectFail() {
124 | mStringBuilder.append("连接失败~~\n");
125 | tv_text.setText(mStringBuilder.toString());
126 | }
127 | });
128 |
129 | **断开连接**
130 |
131 | mBluetoothLe.disconnect();
132 |
133 | **五、发送数据(到蓝牙特征)**
134 |
135 | //以下两个参数为硬件工程师提供,请你与你司的硬件工程师沟通
136 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
137 | private static final String WRITE_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
138 |
139 | mBluetoothLe.writeDataToCharacteristic(bytes, SERVICE_UUID, WRITE_UUID);
140 |
141 | **六、监听发送数据**
142 |
143 | mBluetoothLe.setOnWriteCharacteristicListener(new OnLeWriteCharacteristicListener() {
144 | @Override
145 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
146 | Log.d("debug", "发送了数据:" + Arrays.toString(characteristic.getValue()));
147 | mStringBuilder.append("发送了:").append(Arrays.toString(characteristic.getValue()));
148 | mStringBuilder.append("\n");
149 | tv_text.setText(mStringBuilder.toString());
150 | }
151 |
152 | @Override
153 | public void onFailed(String msg, int status) {
154 |
155 | }
156 | });
157 |
158 | **七、开启通知**
159 |
160 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
161 | private static final String HEART_NOTIFICATION_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
162 | private static final String STEP_NOTIFICATION_UUID = "0000fff3-0000-1000-8000-00805f9b34fb";
163 |
164 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
165 |
166 | **八、开启多个通知**
167 |
168 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
169 |
170 | **九、监听通知**
171 |
172 | mBluetoothLe.setBleNotificationListener(new OnLeNotificationListener() {
173 | @Override
174 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
175 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
176 | }
177 |
178 | @Override
179 | public void onFailure() {
180 |
181 | }
182 | });
183 |
184 | **十、读取数据**
185 |
186 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
187 | private static final String READ_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
188 |
189 | mBluetoothLe.readCharacteristic(SERVICE_UUID, READ_UUID);
190 |
191 | mBluetoothLe.setOnReadCharacteristicListener(new OnLeReadCharacteristicListener() {
192 | @Override
193 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
194 |
195 | }
196 |
197 | @Override
198 | public void onFailure(String info, int status) {
199 |
200 | }
201 | });
202 |
203 |
204 |
205 | **十一、取消队列**
206 |
207 | 假设当你在for循环中发送100条数据,想要在中途取消余下的发送
208 |
209 | mBluetoothLe.clearQueue();
210 |
211 | **十二、关闭GATT**
212 |
213 | 在你退出应用的时候使用
214 |
215 | mBluetoothLe.close();
216 |
217 | ###避免内存泄露
218 |
219 | 在Activity生命周期onDestroy() 中使用:
220 |
221 | mBluetoothLe.destroy();
222 |
223 |
224 | ##仍在补充
225 | 1. 连续操作发送数据、读取特征、开启通知操作设置优先级,像网络请求一样设置优先级
226 | 2. 蓝牙设备信号强度监听
227 | 3. 清理蓝牙缓存
228 | 4. 连接超时设置,连接不上的情况下自动重连的次数设置
229 |
230 | ##了解更多
231 |
232 | 1. See Demo
233 | 2. QQ: 714275846 / 823951895
234 | 3. 邮箱:qindachang@outlook.com
235 | 4. 博客:http://blog.csdn.net/u013003052
236 | 5. Github: https://github.com/qindachang
--------------------------------------------------------------------------------
/document/version-0.2.0.md:
--------------------------------------------------------------------------------
1 | #BluetoothLE library
2 |
3 | [English](https://github.com/qindachang/BluetoothLELibrary/blob/master/README-EN.md "English")
4 |
5 | 低功耗蓝牙框架。**优势**:
6 |
7 | 1. **适配**到Android5.0和Android6.0的扫描方式(速度极快)。
8 | 2. 适配小米手机连接蓝牙操作。
9 | 3. 适配三星手机发现服务、开启通知等。
10 | 4. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
11 | 5. 支持同时**开启多个通知**。
12 | 6. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
13 | 6. 扫描操作支持-> 设置扫描时长、根据设备名称扫描、根据硬件地址扫描、根据服务UUID扫描、连接成功后自动关闭扫描。
14 |
15 |
16 | ###注意点:
17 | 1. Android 6.0扫描蓝牙需要地理位置权限。
18 | 2. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
19 | 3. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
20 |
21 | ##入门指南
22 |
23 | 权限:
24 |
25 |
26 |
27 |
28 |
29 | 6.0以上设备需要
30 |
31 |
32 |
33 |
34 | **引入方式**
35 |
36 | compile 'com.qindachang:BluetoothLELibrary:0.2.0'
37 |
38 | **前戏**
39 |
40 | 判断蓝牙是否打开
41 |
42 | mBluetoothLe.isBluetoothOpen();
43 |
44 | 请求打开蓝牙
45 |
46 | mBluetoothLe.enableBluetooth(activity.this, true);
47 |
48 | 关闭蓝牙
49 |
50 | mBluetoothLe.enableBluetooth(activity.this, false);
51 |
52 |
53 | **一、获取单例实例**
54 |
55 | BluetoothLe mBluetoothLe = BluetoothLe.getDefault();
56 |
57 | **二、初始化**
58 |
59 | mBluetoothLe.init(this);//必须调用init()初始化
60 |
61 | **三、扫描**
62 |
63 | 扫描过程已携带6.0动态权限申请:地理位置权限
64 |
65 | private BluetoothDevice mBluetoothDevice;
66 |
67 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
68 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
69 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
70 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
71 | .startBleScan(this, new OnLeScanListener() {
72 | @Override
73 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
74 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
75 | tv_text.setText(mStringBuilder.toString());
76 | mBluetoothDevice = bluetoothDevice;
77 | }
78 |
79 | @Override
80 | public void onBatchScanResults(List results) {
81 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
82 | tv_text.setText(mStringBuilder.toString());
83 | }
84 |
85 | @Override
86 | public void onScanCompleted() {
87 | mStringBuilder.append("扫描结束\n");
88 | tv_text.setText(mStringBuilder.toString());
89 | }
90 |
91 | @Override
92 | public void onScanFailed(int status) {
93 | mStringBuilder.append("扫描错误\n");
94 | tv_text.setText(mStringBuilder.toString());
95 | }
96 | });
97 |
98 | **四、停止扫描**
99 |
100 | mBluetoothLe.stopBleScan();
101 |
102 | **五、连接蓝牙**
103 |
104 | //发送数据、开启通知等操作,必须等待onServicesDiscovered()发现服务回调后,才能去操作
105 | //参数:false为关闭蓝牙断开自动重连,如果为true则自动断开重连
106 | mBluetoothLe.startBleConnect(false, mBluetoothDevice, new OnLeConnectListener() {
107 |
108 | @Override
109 | public void onDeviceConnecting() {
110 | mStringBuilder.append("连接中...\n");
111 | tv_text.setText(mStringBuilder.toString());
112 | }
113 |
114 | @Override
115 | public void onDeviceConnected() {
116 | mStringBuilder.append("成功连接!\n");
117 | tv_text.setText(mStringBuilder.toString());
118 | }
119 |
120 |
121 | @Override
122 | public void onDeviceDisconnected() {
123 | mStringBuilder.append("断开连接!\n");
124 | tv_text.setText(mStringBuilder.toString());
125 | }
126 |
127 | @Override
128 | public void onServicesDiscovered(BluetoothGatt gatt) {
129 | mStringBuilder.append("发现服务啦\n");
130 | tv_text.setText(mStringBuilder.toString());
131 | }
132 |
133 | @Override
134 | public void onDeviceConnectFail() {
135 | mStringBuilder.append("连接失败~~\n");
136 | tv_text.setText(mStringBuilder.toString());
137 | }
138 | });
139 |
140 | **断开连接**
141 |
142 | mBluetoothLe.disconnect();
143 |
144 | **五、发送数据(到蓝牙特征)**
145 |
146 | //以下两个参数为硬件工程师提供,请你与你司的硬件工程师沟通
147 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
148 | private static final String WRITE_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
149 |
150 | mBluetoothLe.writeDataToCharacteristic(bytes, SERVICE_UUID, WRITE_UUID);
151 |
152 | **六、监听发送数据**
153 |
154 | mBluetoothLe.setOnWriteCharacteristicListener(new OnLeWriteCharacteristicListener() {
155 | @Override
156 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
157 | Log.d("debug", "发送了数据:" + Arrays.toString(characteristic.getValue()));
158 | mStringBuilder.append("发送了:").append(Arrays.toString(characteristic.getValue()));
159 | mStringBuilder.append("\n");
160 | tv_text.setText(mStringBuilder.toString());
161 | }
162 |
163 | @Override
164 | public void onFailed(String msg, int status) {
165 |
166 | }
167 | });
168 |
169 | **七、开启通知**
170 |
171 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
172 | private static final String HEART_NOTIFICATION_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
173 | private static final String STEP_NOTIFICATION_UUID = "0000fff3-0000-1000-8000-00805f9b34fb";
174 |
175 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
176 |
177 | **八、开启多个通知**
178 |
179 | mBluetoothLe.enableBleNotification(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
180 |
181 | **九、监听通知**
182 |
183 | mBluetoothLe.setBleNotificationListener(new OnLeNotificationListener() {
184 | @Override
185 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
186 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
187 | }
188 |
189 | @Override
190 | public void onFailure() {
191 |
192 | }
193 | });
194 |
195 | **十、读取数据**
196 |
197 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
198 | private static final String READ_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
199 |
200 | mBluetoothLe.readCharacteristic(SERVICE_UUID, READ_UUID);
201 |
202 | mBluetoothLe.setOnReadCharacteristicListener(new OnLeReadCharacteristicListener() {
203 | @Override
204 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
205 |
206 | }
207 |
208 | @Override
209 | public void onFailure(String info, int status) {
210 |
211 | }
212 | });
213 |
214 |
215 |
216 | **十一、取消队列**
217 |
218 | 假设当你在for循环中发送100条数据,想要在中途取消余下的发送
219 |
220 | mBluetoothLe.clearQueue();
221 |
222 | **十二、关闭GATT**
223 |
224 | 在你退出应用的时候使用
225 |
226 | mBluetoothLe.close();
227 |
228 | **十三、清理蓝牙缓存**
229 |
230 | 请你在连接上蓝牙后,再执行这步操作
231 |
232 | mBluetoothLe.clearDeviceCache()
233 |
234 |
235 | ###避免内存泄露
236 |
237 | 在Activity生命周期onDestroy() 中使用:
238 |
239 | mBluetoothLe.destroy();
240 |
241 |
242 | ##仍在补充
243 | 1. 连续操作发送数据、读取特征、开启通知操作设置优先级,像网络请求一样设置优先级
244 | 2. 蓝牙设备信号强度监听
245 | 3. 连接超时设置,连接不上的情况下自动重连的次数设置
--------------------------------------------------------------------------------
/document/version-0.3.0.md:
--------------------------------------------------------------------------------
1 | #BluetoothLE library
2 |
3 | [English](https://github.com/qindachang/BluetoothLELibrary/blob/master/README-EN.md "English")
4 |
5 | 低功耗蓝牙库。**优势**:
6 |
7 | 1. **适配**到Android5.0和Android6.0的扫描方式(速度极快)。
8 | 2. 适配小米手机连接蓝牙操作。
9 | 3. 适配三星手机发现服务、开启通知等。
10 | 4. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
11 | 5. 支持同时**开启多个通知**。
12 | 6. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
13 | 6. 扫描操作支持-> 设置扫描时长、根据设备名称扫描、根据硬件地址扫描、根据服务UUID扫描、连接成功后自动关闭扫描。
14 |
15 |
16 | ###注意点:
17 | 1. Android 6.0扫描蓝牙需要地理位置权限。
18 | 2. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
19 | 3. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
20 |
21 | ##入门指南
22 |
23 | 权限:
24 |
25 |
26 |
27 |
28 |
29 | 6.0以上设备需要
30 |
31 |
32 |
33 |
34 | **引入方式**
35 |
36 | compile 'com.qindachang:BluetoothLELibrary:0.3.0'
37 |
38 |
39 | **前戏**
40 |
41 | 判断蓝牙是否打开
42 |
43 | mBluetoothLe.isBluetoothOpen();
44 |
45 | 请求打开蓝牙
46 |
47 | mBluetoothLe.enableBluetooth(activity.this);
48 |
49 | 关闭蓝牙
50 |
51 | mBluetoothLe.disableBluetooth();
52 |
53 |
54 | **一、获取单例实例**
55 |
56 | BluetoothLe mBluetoothLe = BluetoothLe.getDefault();
57 |
58 | **二、初始化**
59 |
60 | mBluetoothLe.init(this);//必须调用init()初始化
61 |
62 | **三、扫描**
63 |
64 | 扫描过程已携带6.0动态权限申请:地理位置权限
65 |
66 | private BluetoothDevice mBluetoothDevice;
67 |
68 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
69 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
70 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
71 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
72 | .startScan(this, new OnLeScanListener() {
73 | @Override
74 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
75 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
76 | tv_text.setText(mStringBuilder.toString());
77 | mBluetoothDevice = bluetoothDevice;
78 | }
79 |
80 | @Override
81 | public void onBatchScanResults(List results) {
82 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
83 | tv_text.setText(mStringBuilder.toString());
84 | }
85 |
86 | @Override
87 | public void onScanCompleted() {
88 | mStringBuilder.append("扫描结束\n");
89 | tv_text.setText(mStringBuilder.toString());
90 | }
91 |
92 | @Override
93 | public void onScanFailed(int status) {
94 | mStringBuilder.append("扫描错误\n");
95 | tv_text.setText(mStringBuilder.toString());
96 | }
97 | });
98 |
99 | 由于使用了单例,单例的生命长度与APP的一致,比activity长。当activity应被回收时,为避免单例的listener回调持有引用,导致activity不能正常被回收,从而引发内存泄露,
100 | 所以在每一个listener前增加一个tag标志,类似于volley,在onDestroy()方法中取消掉对应的tag,可避免内存泄露。
101 |
102 |
103 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
104 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
105 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
106 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
107 | .startScan(TAG, this, new OnLeScanListener() {
108 | @Override
109 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
110 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
111 | tv_text.setText(mStringBuilder.toString());
112 | mBluetoothDevice = bluetoothDevice;
113 | }
114 |
115 | @Override
116 | public void onBatchScanResults(List results) {
117 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
118 | tv_text.setText(mStringBuilder.toString());
119 | }
120 |
121 | @Override
122 | public void onScanCompleted() {
123 | mStringBuilder.append("扫描结束\n");
124 | tv_text.setText(mStringBuilder.toString());
125 | }
126 |
127 | @Override
128 | public void onScanFailed(int status) {
129 | mStringBuilder.append("扫描错误\n");
130 | tv_text.setText(mStringBuilder.toString());
131 | }
132 | });
133 |
134 |
135 | 获取蓝牙扫描状态:
136 |
137 | mBluetoothLe.getScanning();
138 |
139 |
140 | **四、停止扫描**
141 |
142 | mBluetoothLe.stopScan();
143 |
144 | **五、连接蓝牙**
145 |
146 | //发送数据、开启通知等操作,必须等待onServicesDiscovered()发现服务回调后,才能去操作
147 | //参数:false为关闭蓝牙自动重连,如果为true则自动重连
148 | mBluetoothLe.startConnect(false, mBluetoothDevice, new OnLeConnectListener() {
149 |
150 | @Override
151 | public void onDeviceConnecting() {
152 | mStringBuilder.append("连接中...\n");
153 | tv_text.setText(mStringBuilder.toString());
154 | }
155 |
156 | @Override
157 | public void onDeviceConnected() {
158 | mStringBuilder.append("成功连接!\n");
159 | tv_text.setText(mStringBuilder.toString());
160 | }
161 |
162 |
163 | @Override
164 | public void onDeviceDisconnected() {
165 | mStringBuilder.append("断开连接!\n");
166 | tv_text.setText(mStringBuilder.toString());
167 | }
168 |
169 | @Override
170 | public void onServicesDiscovered(BluetoothGatt gatt) {
171 | mStringBuilder.append("发现服务啦\n");
172 | tv_text.setText(mStringBuilder.toString());
173 | }
174 |
175 | @Override
176 | public void onDeviceConnectFail() {
177 | mStringBuilder.append("连接失败~~\n");
178 | tv_text.setText(mStringBuilder.toString());
179 | }
180 | });
181 |
182 | 使用tag:监听连接
183 |
184 | mBluetoothLe.startConnect(false, mBluetoothDevice);
185 |
186 | mBluetoothLe.setOnConnectListener(TAG, new OnLeConnectListener() {
187 | @Override
188 | public void onDeviceConnecting() {
189 | mStringBuilder.append("连接中1");
190 | mStringBuilder.append("\n");
191 | tv_text.setText(mStringBuilder.toString());
192 | }
193 |
194 | @Override
195 | public void onDeviceConnected() {
196 | mStringBuilder.append("已连接1");
197 | mStringBuilder.append("\n");
198 | tv_text.setText(mStringBuilder.toString());
199 | }
200 |
201 | @Override
202 | public void onDeviceDisconnected() {
203 | mStringBuilder.append("断开连接1");
204 | mStringBuilder.append("\n");
205 | tv_text.setText(mStringBuilder.toString());
206 | }
207 |
208 | @Override
209 | public void onServicesDiscovered(BluetoothGatt gatt) {
210 | mStringBuilder.append("发现服务1");
211 | mStringBuilder.append("\n");
212 | tv_text.setText(mStringBuilder.toString());
213 | }
214 |
215 | @Override
216 | public void onDeviceConnectFail() {
217 | mStringBuilder.append("连接失败1");
218 | mStringBuilder.append("\n");
219 | tv_text.setText(mStringBuilder.toString());
220 | }
221 | });
222 |
223 | 获取蓝牙连接状态:
224 |
225 | mBluetoothLe.getConnected();
226 |
227 |
228 | **断开连接**
229 |
230 | mBluetoothLe.disconnect();
231 |
232 | **五、发送数据(到蓝牙特征)**
233 |
234 | //以下两个参数为硬件工程师提供,请你与你司的硬件工程师沟通
235 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
236 | private static final String WRITE_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
237 |
238 | mBluetoothLe.writeDataToCharacteristic(bytes, SERVICE_UUID, WRITE_UUID);
239 |
240 | **六、监听发送数据**
241 |
242 | mBluetoothLe.setOnWriteCharacteristicListener(new OnLeWriteCharacteristicListener() {
243 | @Override
244 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
245 | Log.d("debug", "发送了数据:" + Arrays.toString(characteristic.getValue()));
246 | mStringBuilder.append("发送了:").append(Arrays.toString(characteristic.getValue()));
247 | mStringBuilder.append("\n");
248 | tv_text.setText(mStringBuilder.toString());
249 | }
250 |
251 | @Override
252 | public void onFailed(String msg, int status) {
253 |
254 | }
255 | });
256 |
257 | 使用tag:
258 |
259 | mBluetoothLe.setOnWriteCharacteristicListener(TAG, new OnLeWriteCharacteristicListener() {
260 | @Override
261 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
262 |
263 | }
264 |
265 | @Override
266 | public void onFailed(String msg, int status) {
267 |
268 | }
269 | });
270 |
271 | **七、开启通知**
272 |
273 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
274 | private static final String HEART_NOTIFICATION_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
275 | private static final String STEP_NOTIFICATION_UUID = "0000fff3-0000-1000-8000-00805f9b34fb";
276 |
277 | mBluetoothLe.enableNotification(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
278 |
279 | **八、开启多个通知**
280 |
281 | mBluetoothLe.enableNotification(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
282 |
283 | **九、监听通知**
284 |
285 | mBluetoothLe.setOnNotificationListener(new OnLeNotificationListener() {
286 | @Override
287 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
288 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
289 | }
290 |
291 | });
292 |
293 | 使用tag:
294 |
295 | mBluetoothLe.setOnNotificationListener(TAG, new OnLeNotificationListener() {
296 | @Override
297 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
298 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
299 | }
300 |
301 | });
302 |
303 | **十、读取数据**
304 |
305 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
306 | private static final String READ_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
307 |
308 | mBluetoothLe.readCharacteristic(SERVICE_UUID, READ_UUID);
309 |
310 | 监听读取:
311 |
312 | mBluetoothLe.setOnReadCharacteristicListener(new OnLeReadCharacteristicListener() {
313 | @Override
314 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
315 |
316 | }
317 |
318 | @Override
319 | public void onFailure(String info, int status) {
320 |
321 | }
322 | );
323 |
324 | 使用tag:
325 |
326 | mBluetoothLe.setOnReadCharacteristicListener(TAG, new OnLeReadCharacteristicListener() {
327 | @Override
328 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
329 |
330 | }
331 |
332 | @Override
333 | public void onFailure(String info, int status) {
334 |
335 | }
336 | );
337 |
338 |
339 | **十一、取消队列**
340 |
341 | 假设当你在for循环中发送100条数据,想要在中途取消余下的发送
342 |
343 | mBluetoothLe.clearQueue();
344 |
345 | **十二、关闭GATT**
346 |
347 | 在你退出应用的时候使用
348 |
349 | mBluetoothLe.close();
350 |
351 | **十三、清理蓝牙缓存**
352 |
353 | 请你在连接上蓝牙后,再执行这步操作
354 |
355 | mBluetoothLe.clearDeviceCache()
356 |
357 |
358 | ###避免内存泄露
359 |
360 | 在Activity生命周期onDestroy() 中使用:
361 |
362 | mBluetoothLe.destroy();
363 |
364 | 如果你使用了tag,使用: (它的好处是你可以在多个界面产生多个相同的回调)
365 |
366 | mBluetoothLe.destroy(TAG);
367 |
368 | 取消对应tag:
369 |
370 | mBluetoothLe.cancelTag(TAG);
371 |
372 | 取消全部tag:
373 |
374 | mBluetoothLe.cancelAllTag();
375 |
376 |
377 |
--------------------------------------------------------------------------------
/document/version-0.5.6.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | 
5 | 
6 |
7 | [English](https://github.com/qindachang/BluetoothLELibrary/blob/master/README-EN.md "English") [固件升级/硬件升级/DFU](https://github.com/qindachang/DFUDemo "固件升级/硬件升级/DFU")
8 | [下载jar文件](https://github.com/qindachang/BluetoothLELibrary/blob/master/jars/bluetooth-LE-0.6.0.jar "下载jar文件")
9 |
10 | 低功耗蓝牙库。**优势**:
11 |
12 | 1. **适配**到Android5.0和Android6.0的扫描方式(速度极快)。
13 | 2. 适配小米手机连接蓝牙操作。
14 | 3. 适配三星手机发现服务、开启通知等。
15 | 4. 支持**直接连发数百条**数据,而不用担心消息发不出。自带消息队列(终于可以像iOS一样啦,不用去写延时啦)。
16 | 5. 支持同时**开启多个通知**。
17 | 6. 可以连续操作发送数据、读取特征、开启通知,即使你在for循环中写也没问题,**自带队列**。
18 | 6. 扫描操作支持-> 设置扫描时长、根据设备名称扫描、根据硬件地址扫描、根据服务UUID扫描、连接成功后自动关闭扫描。
19 | 7. 队列定时设置,满足因公司需求蓝牙时间间隔。
20 | 8. 设备信号强度、距离计算回调,可用于防丢器产品。
21 |
22 | ###注意点:
23 | 1. Android 6.0扫描蓝牙需要地理位置权限。
24 | 2. 发送数据、开启通知、读取特征等操作,需要在onServicesDiscovered()发现服务之后才能进行。
25 | 3. 连接设备之前最好先停止扫描(小米手机可能会出现不能发现服务的情况)。
26 |
27 | ##入门指南
28 |
29 | **引入方式**
30 |
31 | 添加依赖
32 |
33 | 作为第一步,依赖这个库添加到您的项目。如何使用库, Gradle是推荐的方式使用这个库的依赖。
34 |
35 | 添加以下代码在你的APP级别 app build.gradle:
36 |
37 | compile 'com.qindachang:BluetoothLELibrary:0.5.6'
38 |
39 | **权限:**
40 |
41 |
42 |
43 |
44 | 6.0以上设备需要
45 |
46 |
47 |
48 |
49 |
50 | **前戏**
51 |
52 | 判断蓝牙是否打开
53 |
54 | mBluetoothLe.isBluetoothOpen();
55 |
56 | 请求打开蓝牙
57 |
58 | mBluetoothLe.enableBluetooth(activity.this);
59 |
60 | 关闭蓝牙
61 |
62 | mBluetoothLe.disableBluetooth();
63 |
64 |
65 | **一、获取单例实例**
66 |
67 | BluetoothLe mBluetoothLe = BluetoothLe.getDefault();
68 |
69 | **二、初始化**
70 |
71 | mBluetoothLe.init(this);//必须调用init()初始化
72 |
73 | 或者使用配置方式进行初始化,这样你可以针对自己的蓝牙产品,做出个性化的蓝牙队列请求。
74 |
75 | such as : 发送队列间隔时间设置,因某些公司蓝牙操作要求时间间隔,例如150ms间隔才能发送下一条数据
76 |
77 | 在Application的onCreate()方法中参照以下方式配置:
78 |
79 | public class BaseApplication extends Application {
80 | @Override
81 | public void onCreate() {
82 | super.onCreate();
83 | BluetoothConfig config = new BluetoothConfig.Builder()
84 | .enableQueueInterval(true)//开启队列定时
85 | .setQueueIntervalTime(150)//设置定时时长(才会发下一条),单位ms
86 | .build();
87 | BluetoothLe.getDefault().init(this, config);
88 | }
89 | }
90 |
91 | 当然,你也可以使用自动的方式来配置蓝牙队列请求。这个时间间隔是通过读取远程蓝牙设备的最小间隔和最大间隔计算得出,保证了队列的最大可用性。
92 |
93 | BluetoothConfig config = new BluetoothConfig.Builder()
94 | .enableQueueInterval(true)
95 | .setQueueIntervalTime(BluetoothConfig.AUTO)
96 | .build();
97 | BluetoothLe.getDefault().init(this, config);
98 |
99 | 上述的读取远程蓝牙设备的最小间隔和最大间隔,你可以在连上蓝牙发现服务后读取:
100 |
101 | @Override
102 | public void onServicesDiscovered(BluetoothGatt gatt) {
103 | mStringBuilder.append("发现服务啦\n");
104 | mTvText.setText(mStringBuilder.toString());
105 |
106 | mHandler.postDelayed(new Runnable() {
107 | @Override
108 | public void run() {
109 | //查看远程蓝牙设备的连接参数,如蓝牙的最小时间间隔和最大时间间隔等..
110 | Log.d("debug", mBluetoothLe.readConnectionParameters().toString());
111 | }
112 | }, 1000);
113 | }
114 |
115 |
116 | 在使用途中修改以上配置:
117 |
118 | 修改配置:
119 |
120 | BluetoothConfig config = new BluetoothConfig.Builder()
121 | .enableQueueInterval(false)
122 | .build();
123 | mBluetoothLe.changeConfig(config);
124 |
125 | **三、扫描**
126 |
127 | 扫描过程已携带6.0动态权限申请:地理位置权限
128 |
129 | mBluetoothLe.setScanPeriod(15000)//设置扫描时长,单位毫秒,默认10秒
130 | .setScanWithDeviceAddress("00:20:ff:34:aa:b3")//根据硬件地址过滤扫描
131 | .setScanWithServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")//设置根据服务uuid过滤扫描
132 | .setScanWithDeviceName("ZG1616")//设置根据设备名称过滤扫描
133 | .setReportDelay(0)//如果为0,则回调onScanResult()方法,如果大于0, 则每隔你设置的时长回调onBatchScanResults()方法,不能小于0
134 | .startScan(this, new OnLeScanListener() {
135 | @Override
136 | public void onScanResult(BluetoothDevice bluetoothDevice, int rssi, ScanRecord scanRecord) {
137 | mStringBuilder.append("扫描到设备:" + bluetoothDevice.getName() + "-信号强度:" + rssi + "\n");
138 | tv_text.setText(mStringBuilder.toString());
139 | mBluetoothDevice = bluetoothDevice;
140 | }
141 |
142 | @Override
143 | public void onBatchScanResults(List results) {
144 | mStringBuilder.append("批处理信息:" + results.toString() + "\n");
145 | tv_text.setText(mStringBuilder.toString());
146 | }
147 |
148 | @Override
149 | public void onScanCompleted() {
150 | mStringBuilder.append("扫描结束\n");
151 | tv_text.setText(mStringBuilder.toString());
152 | }
153 |
154 | @Override
155 | public void onScanFailed(int status) {
156 | mStringBuilder.append("扫描错误\n");
157 | tv_text.setText(mStringBuilder.toString());
158 | }
159 | });
160 |
161 | 根据多个硬件地址、服务uuid、设备名称过滤扫描,你可以这样:
162 |
163 | .setScanWithDeviceAddress(new String[]{"00:20:ff:34:aa:b3","f3:84:55:b4:ab:7f"})
164 | .setScanWithServiceUUID(new String[]{"0000180d-0000-1000-8000-00805f9b34fb","6E400001-B5A3-F393-E0A9-E50E24DCCA9E"})
165 | .setScanWithDeviceName(new String[]{"ZG1616","HaHa"})
166 |
167 | 获取蓝牙扫描状态:
168 |
169 | mBluetoothLe.getScanning();
170 |
171 |
172 | **停止扫描**
173 |
174 | mBluetoothLe.stopScan();
175 |
176 | **四、连接蓝牙、蓝牙连接状态**
177 |
178 | //发送数据、开启通知等操作,必须等待onServicesDiscovered()发现服务回调后,才能去操作
179 | //参数:false为关闭蓝牙自动重连,如果为true则自动重连
180 | mBluetoothLe.setRetryConnectEnable(true)//设置尝试重新连接 ,你可以不使用这个
181 | .setRetryConnectCount(3)//重试连接次数 ,你可以不使用这个
182 | .setConnectTimeOut(5000)//连接超时,单位毫秒 ,你可以不使用这个
183 | .setServiceDiscoverTimeOut(5000)//发现服务超时,单位毫秒 ,你可以不使用这个
184 | .startConnect(false, mBluetoothDevice);
185 |
186 | 由于使用了单例,单例的生命长度与APP的一致,比activity长。当activity应被回收时,为避免单例的listener回调持有引用,导致activity不能正常被回收,从而引发内存泄露,
187 | 所以在每一个listener前增加一个tag标志,类似于volley,在onDestroy()方法中取消掉对应的tag,可避免内存泄露。
188 |
189 | 使用tag:监听连接
190 |
191 | mBluetoothLe.startConnect(false, mBluetoothDevice);
192 |
193 | mBluetoothLe.setOnConnectListener(TAG, new OnLeConnectListener() {
194 | @Override
195 | public void onDeviceConnecting() {
196 | mStringBuilder.append("连接中");
197 | mStringBuilder.append("\n");
198 | tv_text.setText(mStringBuilder.toString());
199 | }
200 |
201 | @Override
202 | public void onDeviceConnected() {
203 | mStringBuilder.append("蓝牙已连接");
204 | mStringBuilder.append("\n");
205 | tv_text.setText(mStringBuilder.toString());
206 | }
207 |
208 | @Override
209 | public void onDeviceDisconnected() {
210 | mStringBuilder.append("蓝牙已断开");
211 | mStringBuilder.append("\n");
212 | tv_text.setText(mStringBuilder.toString());
213 | }
214 |
215 | @Override
216 | public void onServicesDiscovered(BluetoothGatt gatt) {
217 | mStringBuilder.append("发现服务");
218 | mStringBuilder.append("\n");
219 | tv_text.setText(mStringBuilder.toString());
220 | }
221 |
222 | @Override
223 | public void onDeviceConnectFail() {
224 | mStringBuilder.append("连接失败");
225 | mStringBuilder.append("\n");
226 | tv_text.setText(mStringBuilder.toString());
227 | }
228 | });
229 |
230 | 获取蓝牙连接状态:
231 |
232 | mBluetoothLe.getConnected();
233 |
234 | 获取发现服务状态:
235 |
236 | mBluetoothLe.getServicesDiscovered();
237 |
238 |
239 | **断开连接**
240 |
241 | mBluetoothLe.disconnect();
242 |
243 | **五、发送数据(到蓝牙特征)**
244 |
245 | //以下两个参数为硬件工程师提供,请你与你司的硬件工程师沟通
246 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
247 | private static final String WRITE_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
248 |
249 | mBluetoothLe.writeDataToCharacteristic(bytes, SERVICE_UUID, WRITE_UUID);
250 |
251 | 监听发送数据
252 |
253 | 使用tag:
254 |
255 | mBluetoothLe.setOnWriteCharacteristicListener(TAG, new OnLeWriteCharacteristicListener() {
256 | @Override
257 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
258 |
259 | }
260 |
261 | @Override
262 | public void onFailed(String msg, int status) {
263 |
264 | }
265 | });
266 |
267 | **六、Notification类型通知**
268 |
269 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
270 | private static final String HEART_NOTIFICATION_UUID = "00002a37-0000-1000-8000-00805f9b34fb";
271 | private static final String STEP_NOTIFICATION_UUID = "0000fff3-0000-1000-8000-00805f9b34fb";
272 |
273 | 开启一个通知
274 |
275 | mBluetoothLe.enableNotification(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
276 |
277 | 开启多个通知
278 |
279 | mBluetoothLe.enableNotification(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
280 |
281 | 监听通知
282 |
283 | 使用tag:
284 |
285 | mBluetoothLe.setOnNotificationListener(TAG, new OnLeNotificationListener() {
286 | @Override
287 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
288 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
289 | }
290 |
291 | });
292 |
293 | **七、Indication类型通知**
294 |
295 | 开启一个通知
296 |
297 | mBluetoothLe.enableIndication(true, SERVICE_UUID, STEP_NOTIFICATION_UUID);
298 |
299 | 开启多个通知
300 |
301 | mBluetoothLe.enableIndication(true, SERVICE_UUID, new String[]{HEART_NOTIFICATION_UUID, STEP_NOTIFICATION_UUID});
302 |
303 | 监听通知
304 |
305 | 使用tag:
306 |
307 | mBluetoothLe.setOnIndicationListener(TAG, new OnLeIndicationListener() {
308 | @Override
309 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
310 | Log.d("debug", "收到通知:" + Arrays.toString(characteristic.getValue()));
311 | }
312 |
313 | });
314 |
315 |
316 | **八、读取数据**
317 |
318 | private static final String SERVICE_UUID = "0000180d-0000-1000-8000-00805f9b34fb";
319 | private static final String READ_UUID = "0000fff5-0000-1000-8000-00805f9b34fb";
320 |
321 | mBluetoothLe.readCharacteristic(SERVICE_UUID, READ_UUID);
322 |
323 | 监听读取:
324 |
325 | 使用tag:
326 |
327 | mBluetoothLe.setOnReadCharacteristicListener(TAG, new OnLeReadCharacteristicListener() {
328 | @Override
329 | public void onSuccess(BluetoothGattCharacteristic characteristic) {
330 |
331 | }
332 |
333 | @Override
334 | public void onFailure(String info, int status) {
335 |
336 | }
337 | );
338 |
339 | **九、蓝牙信号强度、距离**
340 |
341 | mBluetoothLe.setReadRssiInterval(2000)//设置读取信号强度间隔时间,单位毫秒
342 | .setOnReadRssiListener(TAG, new OnLeReadRssiListener() {
343 | @Override
344 | public void onSuccess(int rssi, int cm) {
345 |
346 | }
347 | });
348 |
349 | 停止监听蓝牙信号强度
350 |
351 | mBluetoothLe.stopReadRssi();
352 |
353 |
354 | **十、清理蓝牙缓存**
355 |
356 | 请你在连接上蓝牙后,再执行这步操作
357 |
358 | mBluetoothLe.clearDeviceCache();
359 |
360 | **十一、关闭GATT**
361 |
362 | 在你退出应用的时候使用
363 |
364 | mBluetoothLe.close();
365 |
366 | **十二、取消队列**
367 |
368 | 假设当你在for循环中发送100条数据,想要在中途取消余下的发送
369 |
370 | mBluetoothLe.clearQueue();
371 |
372 | ###避免内存泄露
373 |
374 | 在Activity生命周期onDestroy() 中使用:
375 |
376 | mBluetoothLe.destroy();
377 |
378 | 如果你使用了tag,使用: (它的好处是你可以在多个界面产生多个相同的回调)
379 |
380 | mBluetoothLe.destroy(TAG);
381 |
382 | 取消对应tag:
383 |
384 | mBluetoothLe.cancelTag(TAG);
385 |
386 | 取消全部tag:
387 |
388 | mBluetoothLe.cancelAllTag();
389 |
390 | ##仍在补充
391 | 1. 用库不star,菊花万人踏
392 | 2. 一连多台蓝牙设备
393 |
394 | ##了解更多
395 |
396 | 1. 强烈建议阅读Demo : [MainActivity.java](https://github.com/qindachang/BluetoothLELibrary/blob/master/app/src/main/java/com/qindachang/bluetoothlelibrary/MainActivity.java "MainActivity.java") / [activity_main.xml](https://github.com/qindachang/BluetoothLELibrary/blob/master/app/src/main/res/layout/activity_main.xml "activity_main.xml")
397 | 2. 邮箱:qindachang@outlook.com
398 | 3. 博客:http://blog.csdn.net/u013003052
399 | 4. Github: https://github.com/qindachang
400 |
401 | ##版本迭代
402 | 1. [Version 0.1.0](https://github.com/qindachang/BluetoothLELibrary/blob/master/document/version-0.1.0.md "Version 0.1.0")
403 | 2. [Version 0.1.1](https://github.com/qindachang/BluetoothLELibrary/blob/master/document/version-0.1.1.md "Version 0.1.1")
404 |
405 | 增加:取消所有队列
406 |
407 | 3. [Version 0.2.0](https://github.com/qindachang/BluetoothLELibrary/blob/master/document/version-0.2.0.md "Version 0.2.0")
408 |
409 | 增加:
410 |
411 | 清理蓝牙缓存;
412 | 判断蓝牙是否打开;
413 | 请求打开蓝牙。
414 |
415 | 4. [Version 0.2.1] 增加获取蓝牙连接状态。
416 | 5. [Version 0.2.2] fix bug.
417 | 6. [Version 0.3.0](https://github.com/qindachang/BluetoothLELibrary/blob/master/document/version-0.3.0.md "Version 0.3.0")
418 |
419 | 增加:类似volley的TAG,可以取消对应TAG的监听,避免内存泄露。
420 | 7. [Version 0.3.2]
421 |
422 | 增加:连接超时设置,连接不上的情况下尝试重连的次数设置,或者发现不了服务情况尝试重连次数
423 |
424 | 8. [Version 0.4.0]
425 |
426 | 增加: Indication类型的通知
427 |
428 | 8. [Version 0.4.1]
429 |
430 | 增加:
431 | 1. 发送队列间隔时间设置,因某些公司蓝牙操作要求时间间隔,例如150ms间隔才能发送下一条数据;
432 | 2. 蓝牙设备信号强度监听,并提供距离计算回调,可用于防丢器
433 |
434 | 8. [Version 0.4.2]
435 |
436 | fix:蓝牙信号强度监听
437 |
438 | 9. [Version 0.5.0]
439 |
440 | 增加:队列时间间隔设置自动,完全可以像iOS一样去操作蓝牙啦
441 |
442 | 10. [Version 0.5.2]
443 |
444 | 过滤扫描可以根据多个uuid等
445 |
446 | ###Thanks
447 |
448 | [NordicSemiconductor](https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library "NordicSemiconductor")
449 |
450 | 1. [philips77](https://github.com/philips77 "philips77")
451 | 2. [aldoborrero](https://github.com/aldoborrero "aldoborrero")
452 |
--------------------------------------------------------------------------------
/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=-Xmx2048m -Dfile.encoding=UTF-8
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 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return status instead of
83 | rem the _cmd.exe /c_ return status!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/image/title.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davistsin/BluetoothLELibrary/b7657c869e570c48a5045f18aa027384d13a75e9/image/title.jpg
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.safeExtGet = {prop, fallback ->
3 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
4 | }
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
9 | }
10 |
11 | plugins {
12 | id 'com.android.library'
13 | id 'maven-publish'
14 | }
15 |
16 | repositories {
17 | google()
18 | mavenCentral()
19 | maven { url 'https://jitpack.io' }
20 | }
21 |
22 | group = 'com.github.davistsin'
23 | version = '1.0.0'
24 |
25 |
26 | android {
27 | compileSdkVersion safeExtGet('compileSdkVersion', 29)
28 |
29 | defaultConfig {
30 | minSdkVersion safeExtGet('minSdkVersion', 18)
31 | targetSdkVersion safeExtGet('targetSdkVersion', 29)
32 | versionCode 1
33 | versionName "1.0.0"
34 | }
35 |
36 | compileOptions {
37 | sourceCompatibility JavaVersion.VERSION_1_8
38 | targetCompatibility JavaVersion.VERSION_1_8
39 | }
40 | }
41 |
42 |
43 |
44 | dependencies {
45 | implementation fileTree(include: ['*.jar'], dir: 'libs')
46 | implementation 'androidx.annotation:annotation:1.2.0'
47 | }
48 |
49 | afterEvaluate {
50 | publishing {
51 | publications {
52 | release(MavenPublication) {
53 | from components.release
54 | groupId = 'com.github.davistsin'
55 | artifactId = 'BluetoothLELibrary'
56 | version = '1.0.0'
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/BleHelper.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle;
2 |
3 | import static android.bluetooth.BluetoothProfile.GATT;
4 |
5 | import android.Manifest;
6 | import android.bluetooth.BluetoothAdapter;
7 | import android.bluetooth.BluetoothDevice;
8 | import android.bluetooth.BluetoothManager;
9 | import android.content.Context;
10 |
11 | import androidx.annotation.RequiresPermission;
12 |
13 | import java.util.List;
14 |
15 |
16 | public class BleHelper {
17 |
18 | @RequiresPermission(Manifest.permission.BLUETOOTH)
19 | public static List getConnectedDevices(Context context) {
20 | BluetoothManager bluetoothManager = (BluetoothManager) context.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
21 | return bluetoothManager.getConnectedDevices(GATT);
22 | }
23 |
24 | public static boolean isSupportBluetooth() {
25 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
26 | return bluetoothAdapter != null;
27 | }
28 |
29 | @RequiresPermission(Manifest.permission.BLUETOOTH)
30 | public static boolean isBluetoothEnable() {
31 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
32 | return bluetoothAdapter.isEnabled();
33 | }
34 |
35 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
36 | public static boolean disableBluetooth() {
37 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
38 | return bluetoothAdapter.disable();
39 | }
40 |
41 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
42 | public static boolean enableBluetooth() {
43 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
44 | return bluetoothAdapter.enable();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/BleConnectCreator.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 | import android.content.Context;
5 |
6 | public final class BleConnectCreator {
7 |
8 | private BleConnectCreator() {}
9 |
10 | public static BleConnector create(Context context, BluetoothDevice device) {
11 | return BleConnectCreator.create(context, device, new ConnectorSettings.Builder().build());
12 | }
13 | public static BleConnector create(Context context, BluetoothDevice device, ConnectorSettings settings) {
14 | return new BleConnector(context, device, settings);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/BleConnector.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | import static com.davistsin.bluetoothle.main.connect.BluetoothUtil.bytes2IntegerList;
4 |
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothGatt;
7 | import android.bluetooth.BluetoothGattCallback;
8 | import android.bluetooth.BluetoothGattCharacteristic;
9 | import android.bluetooth.BluetoothGattDescriptor;
10 | import android.bluetooth.BluetoothGattService;
11 | import android.bluetooth.BluetoothProfile;
12 | import android.content.Context;
13 | import android.os.Handler;
14 | import android.os.Looper;
15 | import android.util.Log;
16 |
17 | import com.davistsin.bluetoothle.main.listener.BleConnectionListener;
18 | import com.davistsin.bluetoothle.main.listener.BleDiscoverServicesListener;
19 | import com.davistsin.bluetoothle.main.listener.BleIndicationListener;
20 | import com.davistsin.bluetoothle.main.listener.BleNotificationListener;
21 | import com.davistsin.bluetoothle.main.listener.BleReadCharacteristicListener;
22 | import com.davistsin.bluetoothle.main.listener.BleRssiListener;
23 | import com.davistsin.bluetoothle.main.listener.BleWriteCharacteristicListener;
24 |
25 | import java.lang.ref.WeakReference;
26 | import java.util.Arrays;
27 | import java.util.List;
28 | import java.util.Queue;
29 | import java.util.Set;
30 | import java.util.UUID;
31 | import java.util.concurrent.CopyOnWriteArraySet;
32 | import java.util.concurrent.LinkedBlockingQueue;
33 |
34 | public class BleConnector {
35 | private static final String TAG = BleConnector.class.getSimpleName();
36 |
37 | private static final UUID SERVICE = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb");
38 | private static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
39 | private static final UUID PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID = UUID.fromString("00002A04-0000-1000-8000-00805f9b34fb");
40 |
41 | private final Set mConnectionListeners = new CopyOnWriteArraySet<>();
42 | private final Set mDiscoverServicesListeners = new CopyOnWriteArraySet<>();
43 | private final Set mWriteCharacteristicListeners = new CopyOnWriteArraySet<>();
44 | private final Set mReadCharacteristicListeners = new CopyOnWriteArraySet<>();
45 | private final Set mNotificationListeners = new CopyOnWriteArraySet<>();
46 | private final Set mIndicationListeners = new CopyOnWriteArraySet<>();
47 | private final Set mRssiListeners = new CopyOnWriteArraySet<>();
48 |
49 | private final Handler mHandler = new Handler(Looper.getMainLooper());
50 | private final RequestQueue mRequestQueue = new RequestQueue();
51 |
52 | private final WeakReference mWeakContext;
53 | private final BluetoothDevice mBluetoothDevice;
54 |
55 | private BluetoothGatt mBluetoothGatt;
56 | private ConnectorSettings mConnectorSettings;
57 | private ConnParameters mConnParameters;
58 |
59 | private volatile boolean connected;
60 |
61 | BleConnector(Context context, BluetoothDevice device, ConnectorSettings settings) {
62 | mWeakContext = new WeakReference<>(context);
63 | mBluetoothDevice = device;
64 | mConnectorSettings = settings;
65 | }
66 |
67 | private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
68 | @Override
69 | public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
70 | super.onPhyUpdate(gatt, txPhy, rxPhy, status);
71 | }
72 |
73 | @Override
74 | public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
75 | super.onPhyRead(gatt, txPhy, rxPhy, status);
76 | }
77 |
78 | @Override
79 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
80 | super.onConnectionStateChange(gatt, status, newState);
81 |
82 | switch (newState) {
83 | case BluetoothProfile.STATE_CONNECTING:
84 | for (BleConnectionListener listener : mConnectionListeners) {
85 | listener.onConnecting();
86 | }
87 | break;
88 | case BluetoothProfile.STATE_CONNECTED:
89 | connected = true;
90 |
91 | if (mConnectorSettings.autoDiscoverServices) {
92 | discoverServices();
93 | }
94 |
95 | for (BleConnectionListener listener : mConnectionListeners) {
96 | listener.onConnected(mBluetoothDevice);
97 | }
98 | break;
99 | case BluetoothProfile.STATE_DISCONNECTED:
100 | connected = false;
101 |
102 | for (BleConnectionListener listener : mConnectionListeners) {
103 | listener.onDisconnected();
104 | }
105 | break;
106 | }
107 | }
108 |
109 | @Override
110 | public void onServicesDiscovered(BluetoothGatt gatt, int status) {
111 | super.onServicesDiscovered(gatt, status);
112 |
113 | if (status == BluetoothGatt.GATT_SUCCESS) {
114 | for (BleDiscoverServicesListener listener : mDiscoverServicesListeners) {
115 | listener.onServicesDiscovered(gatt);
116 | }
117 |
118 | readConnectionParameters();
119 | } else {
120 | for (BleDiscoverServicesListener listener : mDiscoverServicesListeners) {
121 | listener.onFailure();
122 | }
123 | }
124 |
125 | }
126 |
127 | @Override
128 | public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
129 | super.onCharacteristicRead(gatt, characteristic, status);
130 |
131 | switch (status) {
132 | case BluetoothGatt.GATT_SUCCESS:
133 | if (characteristic.getUuid().equals(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID)) {
134 | List parameters = bytes2IntegerList(characteristic.getValue());
135 | double connIntervalMin = (parameters.get(1) * 16 + parameters.get(0)) * 1.25;
136 | double connIntervalMax = (parameters.get(3) * 16 + parameters.get(2)) * 1.25;
137 | int slaveLatency = parameters.get(5) * 16 + parameters.get(4);
138 | int connSupervisionTimeout = parameters.get(7) * 16 + parameters.get(6);
139 | mConnParameters = new ConnParameters();
140 | mConnParameters.setUUID(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID);
141 | mConnParameters.setConnIntervalMin(connIntervalMin);
142 | mConnParameters.setConnIntervalMax(connIntervalMax);
143 | mConnParameters.setProperties("READ");
144 | mConnParameters.setSlaveLatency(slaveLatency);
145 | mConnParameters.setSupervisionTimeout(connSupervisionTimeout);
146 | if (mConnectorSettings.queueIntervalTime == ConnectorSettings.QUEUE_INTERVAL_TIME_AUTO) {
147 | mConnectorSettings.queueIntervalTime = (int) connIntervalMax + 20;
148 | }
149 | } else {
150 | for (BleReadCharacteristicListener listener: mReadCharacteristicListeners) {
151 | listener.onSuccess(characteristic);
152 | }
153 | }
154 | break;
155 | default:
156 | for (BleReadCharacteristicListener listener: mReadCharacteristicListeners) {
157 | listener.onFailure();
158 | }
159 | break;
160 | }
161 | }
162 |
163 | @Override
164 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
165 | super.onCharacteristicWrite(gatt, characteristic, status);
166 |
167 | switch (status) {
168 | case BluetoothGatt.GATT_SUCCESS:
169 | for (BleWriteCharacteristicListener listener: mWriteCharacteristicListeners) {
170 | listener.onSuccess(characteristic);
171 | }
172 | break;
173 | default:
174 | for (BleWriteCharacteristicListener listener: mWriteCharacteristicListeners) {
175 | listener.onFailed();
176 | }
177 | break;
178 | }
179 | }
180 |
181 | @Override
182 | public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
183 | super.onCharacteristicChanged(gatt, characteristic);
184 |
185 | BluetoothGattDescriptor cccd = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
186 | boolean notifications = cccd == null || cccd.getValue() == null || cccd.getValue().length != 2 || cccd.getValue()[0] == 0x01;
187 |
188 |
189 | if (notifications) {
190 | for (BleNotificationListener listener: mNotificationListeners) {
191 | listener.onSuccess(characteristic);
192 | }
193 | } else {
194 | for (BleIndicationListener listener : mIndicationListeners) {
195 | listener.onSuccess(characteristic);
196 | }
197 | }
198 | }
199 |
200 | @Override
201 | public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
202 | super.onDescriptorRead(gatt, descriptor, status);
203 | }
204 |
205 | @Override
206 | public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
207 | super.onDescriptorWrite(gatt, descriptor, status);
208 | }
209 |
210 | @Override
211 | public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
212 | super.onReliableWriteCompleted(gatt, status);
213 | }
214 |
215 | @Override
216 | public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
217 | super.onReadRemoteRssi(gatt, rssi, status);
218 | if (status == BluetoothGatt.GATT_SUCCESS) {
219 | for (BleRssiListener listener : mRssiListeners) {
220 | listener.onSuccess(rssi);
221 | }
222 | }
223 | }
224 | };
225 |
226 | public synchronized boolean connect() {
227 | if (mBluetoothGatt != null) {
228 | return mBluetoothGatt.connect();
229 | }
230 | mBluetoothGatt = mBluetoothDevice.connectGatt(mWeakContext.get(), mConnectorSettings.autoConnect, mGattCallback);
231 | return true;
232 | }
233 |
234 | public synchronized boolean disconnect() {
235 | if (mBluetoothGatt != null) {
236 | mBluetoothGatt.disconnect();
237 | return true;
238 | }
239 | return false;
240 | }
241 |
242 | public synchronized boolean discoverServices() {
243 | return mBluetoothGatt.discoverServices();
244 | }
245 |
246 | public void writeCharacteristic(byte[] bytes, String serviceUUID, String characteristicUUID) {
247 | writeCharacteristic(bytes, UUID.fromString(serviceUUID), UUID.fromString(characteristicUUID));
248 | }
249 |
250 | public void writeCharacteristic(byte[] bytes, UUID serviceUUID, UUID characteristicUUID) {
251 | if (!connected) {
252 | Log.e(TAG, "Error: writeCharacteristic(). Bluetooth not connected.");
253 | return;
254 | }
255 | BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
256 | if (service == null) {
257 | Log.e(TAG, "Error: writeCharacteristic(). Can not find service by UUID: " + serviceUUID.toString());
258 | return;
259 | }
260 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
261 | if (characteristic == null) {
262 | Log.e(TAG, "Error: writeCharacteristic(). Can not find characteristic by UUID: " + serviceUUID.toString());
263 | return;
264 | }
265 | mRequestQueue.addRequest(Request.newWriteRequest(characteristic, bytes));
266 | }
267 |
268 | public void readCharacteristic(String serviceUUID, String characteristicUUID) {
269 | readCharacteristic(UUID.fromString(serviceUUID), UUID.fromString(characteristicUUID));
270 | }
271 |
272 | public void readCharacteristic(UUID serviceUUID, UUID characteristicUUID) {
273 | if (!connected) {
274 | Log.e(TAG, "Error: readCharacteristic(). Bluetooth not connected.");
275 | return;
276 | }
277 | BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
278 | if (service == null) {
279 | Log.e(TAG, "Error: readCharacteristic(). Can not find service by UUID: " + serviceUUID.toString());
280 | return;
281 | }
282 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
283 | if (characteristic == null) {
284 | Log.e(TAG, "Error: readCharacteristic(). Can not find characteristic by UUID: " + serviceUUID.toString());
285 | return;
286 | }
287 | mRequestQueue.addRequest(Request.newReadRequest(characteristic));
288 | }
289 |
290 | public void enableNotification(boolean enable, String serviceUUID, String characteristicUUID) {
291 | enableNotification(enable, serviceUUID, new String[]{characteristicUUID});
292 | }
293 |
294 | public void enableNotification(boolean enable, String serviceUUID, String[] characteristicUUIDs) {
295 | UUID[] uuids = new UUID[characteristicUUIDs.length];
296 | for (int i = 0; i < characteristicUUIDs.length; i++) {
297 | uuids[i] = UUID.fromString(characteristicUUIDs[i]);
298 | }
299 | enableNotification(enable, UUID.fromString(serviceUUID), uuids);
300 | }
301 |
302 | public void enableNotification(boolean enable, UUID serviceUUID, UUID[] characteristicUUIDs) {
303 | if (!connected) {
304 | Log.e(TAG, "Error: enableNotification(). Bluetooth not connected.");
305 | return;
306 | }
307 | BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
308 | if (service == null) {
309 | Log.e(TAG, "Error: enableNotification(). Can not find service by UUID: " + serviceUUID.toString());
310 | return;
311 | }
312 | for (UUID characteristicUUID : characteristicUUIDs) {
313 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
314 | if (characteristic == null) {
315 | Log.e(TAG, "Error: enableNotification(). Can not find characteristic by UUID: " + serviceUUID.toString());
316 | continue;
317 | }
318 | mRequestQueue.addRequest(Request.newEnableNotificationsRequest(enable, characteristic));
319 | }
320 | }
321 |
322 | public void enableIndication(boolean enable, String serviceUUID, String characteristicUUID) {
323 | enableIndication(enable, serviceUUID, new String[]{characteristicUUID});
324 | }
325 |
326 | public void enableIndication(boolean enable, String serviceUUID, String[] characteristicUUIDs) {
327 | UUID[] uuids = new UUID[characteristicUUIDs.length];
328 | for (int i = 0; i < characteristicUUIDs.length; i++) {
329 | uuids[i] = UUID.fromString(characteristicUUIDs[i]);
330 | }
331 | enableIndication(enable, UUID.fromString(serviceUUID), uuids);
332 | }
333 |
334 | public void enableIndication(boolean enable, UUID serviceUUID, UUID[] characteristicUUIDs) {
335 | if (!connected) {
336 | Log.e(TAG, "Error: enableIndication(). Bluetooth not connected.");
337 | return;
338 | }
339 | BluetoothGattService service = mBluetoothGatt.getService(serviceUUID);
340 | if (service == null) {
341 | Log.e(TAG, "Error: enableIndication(). Can not find service by UUID: " + serviceUUID.toString());
342 | return;
343 | }
344 | for (UUID characteristicUUID : characteristicUUIDs) {
345 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUUID);
346 | if (characteristic == null) {
347 | Log.e(TAG, "Error: enableIndication(). Can not find characteristic by UUID: " + serviceUUID.toString());
348 | continue;
349 | }
350 | mRequestQueue.addRequest(Request.newEnableIndicationsRequest(enable, characteristic));
351 | }
352 | }
353 |
354 | public BleConnector addConnectionListener(BleConnectionListener bleConnectionListener) {
355 | mConnectionListeners.add(bleConnectionListener);
356 | return this;
357 | }
358 |
359 | public BleConnector addDiscoveryListener(BleDiscoverServicesListener bleDiscoverServicesListener) {
360 | mDiscoverServicesListeners.add(bleDiscoverServicesListener);
361 | return this;
362 | }
363 |
364 | public BleConnector addWriteCharacteristicListener(BleWriteCharacteristicListener bleWriteCharacteristicListener) {
365 | mWriteCharacteristicListeners.add(bleWriteCharacteristicListener);
366 | return this;
367 | }
368 |
369 | public BleConnector addReadCharacteristicListener(BleReadCharacteristicListener bleReadCharacteristicListener) {
370 | mReadCharacteristicListeners.add(bleReadCharacteristicListener);
371 | return this;
372 | }
373 |
374 | public BleConnector addNotificationListener(BleNotificationListener bleNotificationListener) {
375 | mNotificationListeners.add(bleNotificationListener);
376 | return this;
377 | }
378 |
379 | public BleConnector addIndicationListener(BleIndicationListener bleIndicationListener) {
380 | mIndicationListeners.add(bleIndicationListener);
381 | return this;
382 | }
383 |
384 | public BleConnector addRssiListener(BleRssiListener bleRssiListener) {
385 | mRssiListeners.add(bleRssiListener);
386 | return this;
387 | }
388 |
389 | public BleConnector removeConnectionListeners(BleConnectionListener... listeners) {
390 | mConnectionListeners.removeAll(Arrays.asList(listeners));
391 | return this;
392 | }
393 |
394 | public BleConnector removeDiscoveryListeners(BleDiscoverServicesListener... listeners) {
395 | mDiscoverServicesListeners.removeAll(Arrays.asList(listeners));
396 | return this;
397 | }
398 |
399 | public BleConnector removeNotificationListeners(BleNotificationListener... listeners) {
400 | mNotificationListeners.removeAll(Arrays.asList(listeners));
401 | return this;
402 | }
403 |
404 | public BleConnector removeIndicationListeners(BleIndicationListener... listeners) {
405 | mIndicationListeners.removeAll(Arrays.asList(listeners));
406 | return this;
407 | }
408 |
409 | public BleConnector removeRssiListeners(BleRssiListener... listeners) {
410 | mRssiListeners.removeAll(Arrays.asList(listeners));
411 | return this;
412 | }
413 |
414 | public BleConnector removeAllListeners() {
415 | mConnectionListeners.clear();
416 | mDiscoverServicesListeners.clear();
417 | mNotificationListeners.clear();
418 | mIndicationListeners.clear();
419 | mReadCharacteristicListeners.clear();
420 | mWriteCharacteristicListeners.clear();
421 | mRssiListeners.clear();
422 | return this;
423 | }
424 |
425 | public BluetoothGatt getBluetoothGatt() {
426 | return mBluetoothGatt;
427 | }
428 |
429 | public BluetoothDevice getBluetoothDevice() {
430 | return mBluetoothDevice;
431 | }
432 |
433 | public void changeSettings(ConnectorSettings settings) {
434 | mConnectorSettings = settings;
435 | }
436 |
437 | public boolean getConnected() {
438 | return connected;
439 | }
440 |
441 | public ConnParameters getConnParameters() {
442 | return mConnParameters;
443 | }
444 |
445 | public void close() {
446 | mRequestQueue.cancel();
447 | disconnect();
448 | removeAllListeners();
449 | mBluetoothGatt = null;
450 | }
451 |
452 | private void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
453 | if (mBluetoothGatt == null || characteristic == null) {
454 | return;
455 | }
456 | int properties = characteristic.getProperties();
457 | if ((properties & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) == 0) {
458 | Log.e(TAG, "Error: writeCharacteristic(). " + characteristic.getUuid().toString() + " can not be written");
459 | return;
460 | }
461 | mBluetoothGatt.writeCharacteristic(characteristic);
462 | }
463 |
464 | private void readCharacteristic(BluetoothGattCharacteristic characteristic) {
465 | if (mBluetoothGatt == null || characteristic == null) {
466 | return;
467 | }
468 | int properties = characteristic.getProperties();
469 | if ((properties & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
470 | Log.e(TAG, "Error: readCharacteristic(). " + characteristic.getUuid().toString() + " is not readable");
471 | return;
472 | }
473 | mBluetoothGatt.readCharacteristic(characteristic);
474 | }
475 |
476 | private void enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
477 | if (mBluetoothGatt == null || characteristic == null) {
478 | return;
479 | }
480 | int properties = characteristic.getProperties();
481 | if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
482 | Log.e(TAG, "Error: enableNotification(). " + characteristic.getUuid().toString() + " not supports notification");
483 | return;
484 | }
485 | mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
486 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
487 | if (descriptor == null) {
488 | return;
489 | }
490 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
491 | mBluetoothGatt.writeDescriptor(descriptor);
492 | }
493 |
494 | private void enableIndication(boolean enable, BluetoothGattCharacteristic characteristic) {
495 | if (mBluetoothGatt == null || characteristic == null) {
496 | return;
497 | }
498 | int properties = characteristic.getProperties();
499 | if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == 0) {
500 | Log.e(TAG, "Error: enableIndication(). " + characteristic.getUuid().toString() + " not supports indication");
501 | return;
502 | }
503 | mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
504 | BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
505 | if (descriptor == null) {
506 | return;
507 | }
508 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
509 | mBluetoothGatt.writeDescriptor(descriptor);
510 | }
511 |
512 | /**
513 | * 读取已连接设备的一些硬件信息
514 | */
515 | private void readConnectionParameters() {
516 | if (mBluetoothGatt != null) {
517 | BluetoothGattService service = mBluetoothGatt.getService(SERVICE);
518 | if (service != null) {
519 | BluetoothGattCharacteristic characteristic = service.getCharacteristic(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID);
520 | if (characteristic != null) {
521 | mRequestQueue.addRequest(Request.newReadRequest(characteristic));
522 | }
523 | }
524 | }
525 | }
526 |
527 | private class RequestQueue {
528 | private final Queue mRequestQueue = new LinkedBlockingQueue<>();
529 |
530 | synchronized void addRequest(Request request) {
531 | int oldSize = mRequestQueue.size();
532 | mRequestQueue.add(request);
533 | if (mRequestQueue.size() == 1 && oldSize == 0) {
534 | startExecutor();
535 | }
536 | }
537 |
538 | private void startExecutor() {
539 | Request request = mRequestQueue.peek();
540 | if (request == null) {
541 | return;
542 | }
543 | switch (request.type) {
544 | case WRITE:
545 | BluetoothGattCharacteristic characteristic = request.getCharacteristic();
546 | characteristic.setValue(request.getBytes());
547 | writeCharacteristic(characteristic);
548 | break;
549 | case READ:
550 | readCharacteristic(request.getCharacteristic());
551 | break;
552 | case ENABLE_NOTIFICATIONS:
553 | enableNotification(request.isEnable(), request.getCharacteristic());
554 | break;
555 | case ENABLE_INDICATIONS:
556 | enableIndication(request.isEnable(), request.getCharacteristic());
557 | break;
558 | }
559 | next();
560 | }
561 |
562 | void next() {
563 | if (mConnectorSettings.enableQueue && mConnectorSettings.queueIntervalTime > 0) {
564 | mHandler.postDelayed(new Runnable() {
565 | @Override
566 | public void run() {
567 | runQueue();
568 | }
569 | }, mConnectorSettings.queueIntervalTime);
570 | } else {
571 | runQueue();
572 | }
573 | }
574 |
575 | void runQueue() {
576 | mRequestQueue.poll();
577 | if (mRequestQueue.size() > 0) {
578 | startExecutor();
579 | }
580 | }
581 |
582 | void cancel() {
583 | mRequestQueue.clear();
584 | }
585 | }
586 | }
587 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/BluetoothUtil.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | class BluetoothUtil {
7 |
8 | static int getDistance(int rssi) {
9 | int irssi = Math.abs(rssi);
10 | double power = (irssi - 70.0) / (10 * 2.0);
11 | power = Math.pow(10d, power);
12 | power = power * 100;
13 | return (int) power;
14 | }
15 |
16 | static List bytes2IntegerList(byte[] bytes) {
17 | List list = new ArrayList<>();
18 | for (byte aByte : bytes) {
19 | int v = aByte & 0xFF;
20 | list.add(v);
21 | }
22 | return list;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/ConnParameters.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import java.util.UUID;
7 |
8 | public class ConnParameters implements Parcelable {
9 |
10 | private UUID mUUID = UUID.fromString("00002A04-0000-1000-8000-00805f9b34fb");
11 | private String mProperties = "";
12 | private double connIntervalMin;
13 | private double connIntervalMax;
14 | private int slaveLatency;
15 | private int supervisionTimeout;
16 |
17 | public UUID getUUID() {
18 | return mUUID;
19 | }
20 |
21 | public void setUUID(UUID UUID) {
22 | mUUID = UUID;
23 | }
24 |
25 | public String getProperties() {
26 | return mProperties;
27 | }
28 |
29 | public void setProperties(String properties) {
30 | mProperties = properties;
31 | }
32 |
33 | public double getConnIntervalMin() {
34 | return connIntervalMin;
35 | }
36 |
37 | public void setConnIntervalMin(double connIntervalMin) {
38 | this.connIntervalMin = connIntervalMin;
39 | }
40 |
41 | public double getConnIntervalMax() {
42 | return connIntervalMax;
43 | }
44 |
45 | public void setConnIntervalMax(double connIntervalMax) {
46 | this.connIntervalMax = connIntervalMax;
47 | }
48 |
49 | public int getSlaveLatency() {
50 | return slaveLatency;
51 | }
52 |
53 | public void setSlaveLatency(int slaveLatency) {
54 | this.slaveLatency = slaveLatency;
55 | }
56 |
57 | public int getSupervisionTimeout() {
58 | return supervisionTimeout;
59 | }
60 |
61 | public void setSupervisionTimeout(int supervisionTimeout) {
62 | this.supervisionTimeout = supervisionTimeout;
63 | }
64 |
65 | @Override
66 | public String toString() {
67 | return "ConnParameters:{UUID = " + mUUID.toString() + ", Connection Interval Min = " + connIntervalMin
68 | + ", Connection Interval Max = " + connIntervalMax
69 | + ", Slave Latency = " + slaveLatency + ", Supervision Timeout Multiplier = "
70 | + supervisionTimeout + "}";
71 | }
72 |
73 | @Override
74 | public int describeContents() {
75 | return 0;
76 | }
77 |
78 | @Override
79 | public void writeToParcel(Parcel dest, int flags) {
80 | dest.writeSerializable(this.mUUID);
81 | dest.writeString(this.mProperties);
82 | dest.writeDouble(this.connIntervalMin);
83 | dest.writeDouble(this.connIntervalMax);
84 | dest.writeInt(this.slaveLatency);
85 | dest.writeInt(this.supervisionTimeout);
86 | }
87 |
88 | public ConnParameters() {
89 | }
90 |
91 | protected ConnParameters(Parcel in) {
92 | this.mUUID = (UUID) in.readSerializable();
93 | this.mProperties = in.readString();
94 | this.connIntervalMin = in.readDouble();
95 | this.connIntervalMax = in.readDouble();
96 | this.slaveLatency = in.readInt();
97 | this.supervisionTimeout = in.readInt();
98 | }
99 |
100 | public static final Creator CREATOR = new Creator() {
101 | @Override
102 | public ConnParameters createFromParcel(Parcel source) {
103 | return new ConnParameters(source);
104 | }
105 |
106 | @Override
107 | public ConnParameters[] newArray(int size) {
108 | return new ConnParameters[size];
109 | }
110 | };
111 | }
112 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/ConnectorSettings.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | public class ConnectorSettings {
4 |
5 | public static int QUEUE_INTERVAL_TIME_AUTO = Integer.MIN_VALUE;
6 |
7 | volatile boolean autoConnect;
8 | volatile boolean autoDiscoverServices;
9 | volatile boolean enableQueue;
10 | volatile int queueIntervalTime;
11 |
12 | private ConnectorSettings(ConnectorSettings.Builder builder) {
13 | this.autoConnect = builder.autoConnect;
14 | this.autoDiscoverServices = builder.autoDiscoverServices;
15 | this.enableQueue = builder.enableQueue;
16 | this.queueIntervalTime = builder.queueIntervalTime;
17 | }
18 |
19 | public static class Builder {
20 | private boolean autoConnect = false;
21 | private boolean autoDiscoverServices = true;
22 | private boolean enableQueue = true;
23 | private int queueIntervalTime = QUEUE_INTERVAL_TIME_AUTO;
24 |
25 | /**
26 | * 自动重连。
27 | * @param value
28 | * @return
29 | */
30 | public Builder autoConnect(boolean value) {
31 | autoConnect = value;
32 | return this;
33 | }
34 |
35 | /**
36 | * 连接成功后,自动发现服务。发现服务后,才能对服务下的特征进行操作。
37 | * @param value
38 | * @return
39 | */
40 | public Builder autoDiscoverServices(boolean value) {
41 | autoDiscoverServices = value;
42 | return this;
43 | }
44 |
45 | /**
46 | * 启用队列操作。
47 | * @param enable
48 | * @return
49 | */
50 | public Builder enableQueue(boolean enable) {
51 | enableQueue = enable;
52 | return this;
53 | }
54 |
55 | /**
56 | * 队列时间间隔。
57 | * @param milliseconds ConnectorSettings.QUEUE_INTERVAL_TIME_AUTO 自动读取硬件信息获得间隔时间。或者手动毫秒。
58 | * @return
59 | */
60 | public Builder setQueueIntervalTime(int milliseconds) {
61 | queueIntervalTime = milliseconds;
62 | return this;
63 | }
64 |
65 | public ConnectorSettings build() {
66 | return new ConnectorSettings(this);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/connect/Request.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.connect;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | /* package */ class Request {
6 |
7 | private byte[] mBytes;
8 |
9 | enum Type {
10 | WRITE,
11 | READ,
12 | ENABLE_NOTIFICATIONS,
13 | ENABLE_INDICATIONS
14 | }
15 |
16 | public final Type type;
17 | private final BluetoothGattCharacteristic characteristic;
18 | private boolean enable;
19 |
20 | private Request(final Type type, final BluetoothGattCharacteristic characteristic) {
21 | this.type = type;
22 | this.characteristic = characteristic;
23 | }
24 |
25 | private Request(final Type type, final BluetoothGattCharacteristic characteristic, byte[] bytes) {
26 | this.type = type;
27 | this.characteristic = characteristic;
28 | this.mBytes = bytes;
29 | }
30 |
31 | private Request(final Type type, final BluetoothGattCharacteristic characteristic, boolean enable) {
32 | this.type = type;
33 | this.characteristic = characteristic;
34 | this.enable = enable;
35 | }
36 |
37 | public static Request newReadRequest(final BluetoothGattCharacteristic characteristic) {
38 | return new Request(Type.READ, characteristic);
39 | }
40 |
41 | public static Request newWriteRequest(final BluetoothGattCharacteristic characteristic, byte[] bytes) {
42 | return new Request(Type.WRITE, characteristic, bytes);
43 | }
44 |
45 | public static Request newEnableNotificationsRequest(final boolean enable, final BluetoothGattCharacteristic characteristic) {
46 | return new Request(Type.ENABLE_NOTIFICATIONS, characteristic, enable);
47 | }
48 |
49 | public static Request newEnableIndicationsRequest(final boolean enable, final BluetoothGattCharacteristic characteristic) {
50 | return new Request(Type.ENABLE_INDICATIONS, characteristic, enable);
51 | }
52 |
53 |
54 | public BluetoothGattCharacteristic getCharacteristic() {
55 | return characteristic;
56 | }
57 |
58 | public byte[] getBytes() {
59 | return mBytes;
60 | }
61 |
62 | public boolean isEnable() {
63 | return enable;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleConnectionListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 |
5 | public interface BleConnectionListener {
6 | void onConnecting();
7 |
8 | void onConnected(BluetoothDevice device);
9 |
10 | void onDisconnected();
11 |
12 | void onFailure();
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleDiscoverServicesListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothGatt;
4 |
5 | public interface BleDiscoverServicesListener {
6 |
7 | void onServicesDiscovered(BluetoothGatt gatt);
8 |
9 | void onFailure();
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleIndicationListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | import com.qindachang.bluetoothle.exception.BleException;
6 |
7 | public interface BleIndicationListener {
8 | void onSuccess(BluetoothGattCharacteristic characteristic);
9 | void onFailed();
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleNotificationListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | public interface BleNotificationListener {
6 | void onSuccess(BluetoothGattCharacteristic characteristic);
7 | void onFailed();
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleReadCharacteristicListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | public interface BleReadCharacteristicListener {
6 | void onSuccess(BluetoothGattCharacteristic characteristic);
7 |
8 | void onFailure();
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleRssiListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | public interface BleRssiListener {
4 | void onSuccess(int rssi);
5 | }
6 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/main/listener/BleWriteCharacteristicListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.main.listener;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | public interface BleWriteCharacteristicListener {
6 | void onSuccess(BluetoothGattCharacteristic characteristic);
7 |
8 | void onFailed();
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/BleGattServer.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.Manifest;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothGatt;
7 | import android.bluetooth.BluetoothGattCharacteristic;
8 | import android.bluetooth.BluetoothGattDescriptor;
9 | import android.bluetooth.BluetoothGattServer;
10 | import android.bluetooth.BluetoothGattServerCallback;
11 | import android.bluetooth.BluetoothGattService;
12 | import android.bluetooth.BluetoothManager;
13 | import android.bluetooth.BluetoothProfile;
14 | import android.bluetooth.le.AdvertiseCallback;
15 | import android.bluetooth.le.AdvertiseData;
16 | import android.bluetooth.le.AdvertiseSettings;
17 | import android.bluetooth.le.BluetoothLeAdvertiser;
18 | import android.content.Context;
19 | import android.os.Build;
20 | import android.os.ParcelUuid;
21 | import android.util.Log;
22 |
23 | import androidx.annotation.RequiresApi;
24 | import androidx.annotation.RequiresPermission;
25 |
26 | import java.util.ArrayList;
27 | import java.util.Arrays;
28 | import java.util.List;
29 | import java.util.Set;
30 | import java.util.UUID;
31 | import java.util.concurrent.CopyOnWriteArraySet;
32 |
33 | /**
34 | * BLE从机
35 | */
36 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
37 | public final class BleGattServer {
38 | private static final String TAG = BleGattServer.class.getSimpleName();
39 | public static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
40 |
41 | private final List mConnectDevices = new ArrayList<>();
42 |
43 | private final Set mOnAdvertiseListeners = new CopyOnWriteArraySet<>();
44 | private final Set mOnConnectionStateChangeListeners = new CopyOnWriteArraySet<>();
45 | private final Set mOnServiceAddedListeners = new CopyOnWriteArraySet<>();
46 | private final Set mOnWriteRequestListeners = new CopyOnWriteArraySet<>();
47 | private final Set mOnReadRequestListeners = new CopyOnWriteArraySet<>();
48 |
49 | private BluetoothGattServer mBluetoothGattServer;
50 | private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
51 |
52 | private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
53 | @Override
54 | public void onStartSuccess(final AdvertiseSettings settingsInEffect) {
55 | super.onStartSuccess(settingsInEffect);
56 |
57 | for (OnAdvertiseListener listener : mOnAdvertiseListeners) {
58 | listener.onStartSuccess(settingsInEffect);
59 | }
60 |
61 | }
62 |
63 | @Override
64 | public void onStartFailure(final int errorCode) {
65 | super.onStartFailure(errorCode);
66 |
67 | for (OnAdvertiseListener listener : mOnAdvertiseListeners) {
68 | listener.onStartFailure(errorCode);
69 | }
70 |
71 | }
72 | };
73 |
74 | private final BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {
75 | @Override
76 | public void onConnectionStateChange(final BluetoothDevice device, final int status, final int newState) {
77 | super.onConnectionStateChange(device, status, newState);
78 | if (newState == BluetoothProfile.STATE_CONNECTED) {
79 | mConnectDevices.add(device);
80 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
81 | mConnectDevices.remove(device);
82 | }
83 |
84 | for (OnConnectionStateChangeListener listener : mOnConnectionStateChangeListeners) {
85 | if (newState == BluetoothProfile.STATE_CONNECTED) {
86 | listener.onConnected(device);
87 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
88 | listener.onDisconnected(device);
89 | }
90 | listener.onChange(device, status, newState);
91 | }
92 |
93 | }
94 |
95 | @Override
96 | public void onServiceAdded(final int status, final BluetoothGattService service) {
97 | super.onServiceAdded(status, service);
98 |
99 | for (OnServiceAddedListener listener : mOnServiceAddedListeners) {
100 | if (status == BluetoothGatt.GATT_SUCCESS) {
101 | listener.onSuccess(service);
102 | } else {
103 | listener.onFail(service);
104 | }
105 | }
106 |
107 | }
108 |
109 | @Override
110 | public void onCharacteristicReadRequest(final BluetoothDevice device, int requestId, int offset, final BluetoothGattCharacteristic characteristic) {
111 | super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
112 | Log.d(TAG, "onCharacteristicReadRequest : " + Arrays.toString(characteristic.getValue()));
113 | mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, characteristic.getValue());
114 |
115 | for (OnReadRequestListener listener : mOnReadRequestListeners) {
116 | listener.onCharacteristicRead(device, characteristic);
117 | }
118 | }
119 |
120 | @Override
121 | public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId, final BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, final byte[] value) {
122 | super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
123 | Log.d(TAG, "onCharacteristicWriteRequest : " + Arrays.toString(value));
124 | mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value);
125 |
126 | for (OnWriteRequestListener listener : mOnWriteRequestListeners) {
127 | listener.onCharacteristicWritten(device, characteristic, value);
128 | }
129 | }
130 |
131 | @Override
132 | public void onDescriptorReadRequest(final BluetoothDevice device, int requestId, int offset, final BluetoothGattDescriptor descriptor) {
133 | super.onDescriptorReadRequest(device, requestId, offset, descriptor);
134 | Log.d(TAG, "onDescriptorReadRequest : " + Arrays.toString(descriptor.getValue()));
135 | mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, descriptor.getValue());
136 |
137 | for (OnReadRequestListener listener : mOnReadRequestListeners) {
138 | listener.onDescriptorRead(device, descriptor);
139 | }
140 | }
141 |
142 | @Override
143 | public void onDescriptorWriteRequest(final BluetoothDevice device, int requestId, final BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, final byte[] value) {
144 | super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
145 | Log.d(TAG, "onDescriptorWriteRequest : " + Arrays.toString(value));
146 | mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, value);
147 |
148 | for (OnWriteRequestListener listener : mOnWriteRequestListeners) {
149 | listener.onDescriptorWritten(device, descriptor, value);
150 | }
151 |
152 | }
153 |
154 | @Override
155 | public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
156 | super.onExecuteWrite(device, requestId, execute);
157 | }
158 |
159 | @Override
160 | public void onNotificationSent(BluetoothDevice device, int status) {
161 | super.onNotificationSent(device, status);
162 | }
163 |
164 | @Override
165 | public void onMtuChanged(BluetoothDevice device, int mtu) {
166 | super.onMtuChanged(device, mtu);
167 | }
168 | };
169 |
170 | /**
171 | * Open the advertising, let other phone can search for this device using Bluetooth.
172 | *
173 | * @param serviceUuid Parameters needed for other Bluetooth devices to filter and scan.
174 | * @return Success or failure
175 | */
176 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
177 | public boolean startAdvertising(UUID serviceUuid) {
178 | BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
179 | mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
180 | if (mBluetoothLeAdvertiser == null) {
181 | Log.e(TAG, "Failed to create advertiser");
182 | return false;
183 | }
184 |
185 | AdvertiseSettings advertiseSettings = new AdvertiseSettings.Builder()
186 | .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
187 | .setConnectable(true)
188 | .setTimeout(0)
189 | .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
190 | .build();
191 |
192 | AdvertiseData advertiseData = new AdvertiseData.Builder()
193 | .setIncludeDeviceName(true)
194 | .setIncludeTxPowerLevel(false)
195 | .addServiceUuid(new ParcelUuid(serviceUuid))
196 | .build();
197 |
198 | mBluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertiseData, mAdvertiseCallback);
199 | return true;
200 | }
201 |
202 | /**
203 | * Stop Bluetooth LE advertising.
204 | */
205 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
206 | public boolean stopAdvertising() {
207 | if (mBluetoothLeAdvertiser == null) {
208 | return false;
209 | }
210 | mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
211 | for (OnAdvertiseListener listener : mOnAdvertiseListeners) {
212 | listener.onStopAdvertising();
213 | }
214 | return true;
215 | }
216 |
217 | public boolean startServer(Context context) {
218 | BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
219 | if (bluetoothManager == null) {
220 | return false;
221 | }
222 | mBluetoothGattServer = bluetoothManager.openGattServer(context, mBluetoothGattServerCallback);
223 | return true;
224 | }
225 |
226 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
227 | public boolean closeServer() {
228 | removeAllListeners();
229 | if (mBluetoothGattServer == null) {
230 | return false;
231 | }
232 | stopAdvertising();
233 | mBluetoothGattServer.close();
234 | return true;
235 | }
236 |
237 | public void addService(ServiceSettings settings) {
238 | BluetoothGattService service = new BluetoothGattService(settings.serviceUuid, settings.serviceType);
239 | List profiles = settings.serviceProfiles;
240 | for (ServiceProfile profile : profiles) {
241 | BluetoothGattCharacteristic characteristic =
242 | new BluetoothGattCharacteristic(profile.getCharacteristicUuid(), profile.getCharacteristicProperties(), profile.getCharacteristicPermission());
243 | if (profile.getDescriptorUuid() != null) {
244 | BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(profile.getDescriptorUuid(), profile.getDescriptorPermission());
245 | descriptor.setValue(profile.getDescriptorValue());
246 | characteristic.addDescriptor(descriptor);
247 | }
248 | service.addCharacteristic(characteristic);
249 | }
250 | mBluetoothGattServer.addService(service);
251 | }
252 |
253 | public void removeService(BluetoothGattService service) {
254 | mBluetoothGattServer.removeService(service);
255 | }
256 |
257 | public List getServices() {
258 | return mBluetoothGattServer.getServices();
259 | }
260 |
261 | public BluetoothGattServer getBluetoothGattServer() {
262 | return mBluetoothGattServer;
263 | }
264 |
265 | public List getConnectDevices() {
266 | return mConnectDevices;
267 | }
268 |
269 | public void sendReadCharacteristic(UUID serviceUuid, UUID characteristicUuid, byte[] value) {
270 | BluetoothGattCharacteristic characteristic = mBluetoothGattServer
271 | .getService(serviceUuid)
272 | .getCharacteristic(characteristicUuid);
273 | characteristic.setValue(value);
274 | }
275 |
276 | public void sendNotificationValue(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid, byte[] value) {
277 | BluetoothGattCharacteristic characteristic = mBluetoothGattServer
278 | .getService(serviceUuid)
279 | .getCharacteristic(characteristicUuid);
280 | characteristic.setValue(value);
281 | mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
282 | }
283 |
284 | public void sendIndicationValue(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid, byte[] value) {
285 | BluetoothGattCharacteristic characteristic = mBluetoothGattServer
286 | .getService(serviceUuid)
287 | .getCharacteristic(characteristicUuid);
288 | characteristic.setValue(value);
289 | mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, true);
290 | }
291 |
292 | public void addOnAdvertiseListener(OnAdvertiseListener listener) {
293 | mOnAdvertiseListeners.add(listener);
294 | }
295 |
296 | public void removeAdvertiseStartListener(OnAdvertiseListener listener) {
297 | mOnAdvertiseListeners.remove(listener);
298 | }
299 |
300 | public void addOnConnectionStateChangeListener(OnConnectionStateChangeListener listener) {
301 | mOnConnectionStateChangeListeners.add(listener);
302 | }
303 |
304 | public void removeConnectionStateChangeListener(OnConnectionStateChangeListener listener) {
305 | mOnConnectionStateChangeListeners.remove(listener);
306 | }
307 |
308 | public void addOnServiceAddedListener(OnServiceAddedListener listener) {
309 | mOnServiceAddedListeners.add(listener);
310 | }
311 |
312 | public void removeServiceAddedListener(OnServiceAddedListener listener) {
313 | mOnServiceAddedListeners.remove(listener);
314 | }
315 |
316 | public void addOnWriteRequestListener(OnWriteRequestListener listener) {
317 | mOnWriteRequestListeners.add(listener);
318 | }
319 |
320 | public void removeWriteRequestListener(OnWriteRequestListener listener) {
321 | mOnWriteRequestListeners.remove(listener);
322 | }
323 |
324 | public void addOnReadRequestListener(OnReadRequestListener listener) {
325 | mOnReadRequestListeners.add(listener);
326 | }
327 |
328 | public void removeReadRequestListener(OnReadRequestListener listener) {
329 | mOnReadRequestListeners.remove(listener);
330 | }
331 |
332 | public void removeAllListeners() {
333 | mOnAdvertiseListeners.clear();
334 | mOnConnectionStateChangeListeners.clear();
335 | mOnServiceAddedListeners.clear();
336 | mOnWriteRequestListeners.clear();
337 | mOnReadRequestListeners.clear();
338 | }
339 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/IServerListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | /**
4 | * Created by David Qin on 2017/4/21.
5 | */
6 |
7 | interface IServerListener {
8 | // nothing, it just a present
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/OnAdvertiseListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.bluetooth.le.AdvertiseSettings;
4 |
5 | /**
6 | * Created by David Qin on 2017/4/19.
7 | */
8 |
9 | public interface OnAdvertiseListener extends IServerListener {
10 |
11 | /**
12 | * @param settingsInEffect The actual settings used for advertising, which may be different from
13 | * what has been requested.
14 | */
15 | void onStartSuccess(AdvertiseSettings settingsInEffect);
16 |
17 | /**
18 | * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start
19 | * failures.
20 | */
21 | void onStartFailure(int errorCode);
22 |
23 | void onStopAdvertising();
24 | }
25 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/OnConnectionStateChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 |
5 | /**
6 | * Created by David Qin on 2017/4/20.
7 | */
8 |
9 | public interface OnConnectionStateChangeListener extends IServerListener {
10 | void onChange(BluetoothDevice device, int status, int newState);
11 |
12 | void onConnected(BluetoothDevice device);
13 |
14 | void onDisconnected(BluetoothDevice device);
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/OnReadRequestListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 | import android.bluetooth.BluetoothGattCharacteristic;
5 | import android.bluetooth.BluetoothGattDescriptor;
6 |
7 | /**
8 | * Created by David Qin on 2017/4/20.
9 | */
10 |
11 | public interface OnReadRequestListener extends IServerListener {
12 | void onCharacteristicRead(BluetoothDevice device, BluetoothGattCharacteristic characteristic);
13 |
14 | void onDescriptorRead(BluetoothDevice device, BluetoothGattDescriptor descriptor);
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/OnServiceAddedListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.bluetooth.BluetoothGattService;
4 |
5 | /**
6 | * Created by David Qin on 2017/4/21.
7 | */
8 |
9 | public interface OnServiceAddedListener extends IServerListener {
10 | void onSuccess(BluetoothGattService service);
11 |
12 | void onFail(BluetoothGattService service);
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/OnWriteRequestListener.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 | import android.bluetooth.BluetoothGattCharacteristic;
5 | import android.bluetooth.BluetoothGattDescriptor;
6 |
7 | /**
8 | * Created by David Qin on 2017/4/20.
9 | */
10 |
11 | public interface OnWriteRequestListener extends IServerListener {
12 | void onCharacteristicWritten(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value);
13 |
14 | void onDescriptorWritten(BluetoothDevice device, BluetoothGattDescriptor descriptor, byte[] value);
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/ServiceProfile.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import java.util.Arrays;
7 | import java.util.UUID;
8 |
9 | /**
10 | * Created by David Qin on 2017/4/19.
11 | */
12 |
13 | public class ServiceProfile implements Parcelable {
14 | private UUID characteristicUuid;
15 | private int characteristicProperties;
16 | private int characteristicPermission;
17 |
18 | private UUID descriptorUuid;
19 | private int descriptorPermission;
20 | private byte[] descriptorValue;
21 |
22 | public UUID getCharacteristicUuid() {
23 | return characteristicUuid;
24 | }
25 |
26 | public void setCharacteristicUuid(UUID characteristicUuid) {
27 | this.characteristicUuid = characteristicUuid;
28 | }
29 |
30 | public int getCharacteristicProperties() {
31 | return characteristicProperties;
32 | }
33 |
34 | public void setCharacteristicProperties(int characteristicProperties) {
35 | this.characteristicProperties = characteristicProperties;
36 | }
37 |
38 | public int getCharacteristicPermission() {
39 | return characteristicPermission;
40 | }
41 |
42 | public void setCharacteristicPermission(int characteristicPermission) {
43 | this.characteristicPermission = characteristicPermission;
44 | }
45 |
46 | public UUID getDescriptorUuid() {
47 | return descriptorUuid;
48 | }
49 |
50 | public void setDescriptorUuid(UUID descriptorUuid) {
51 | this.descriptorUuid = descriptorUuid;
52 | }
53 |
54 | public int getDescriptorPermission() {
55 | return descriptorPermission;
56 | }
57 |
58 | public void setDescriptorPermission(int descriptorPermission) {
59 | this.descriptorPermission = descriptorPermission;
60 | }
61 |
62 | public byte[] getDescriptorValue() {
63 | return descriptorValue;
64 | }
65 |
66 | public void setDescriptorValue(byte[] descriptorValue) {
67 | this.descriptorValue = descriptorValue;
68 | }
69 |
70 |
71 | @Override
72 | public int describeContents() {
73 | return 0;
74 | }
75 |
76 | @Override
77 | public void writeToParcel(Parcel dest, int flags) {
78 | dest.writeSerializable(this.characteristicUuid);
79 | dest.writeInt(this.characteristicProperties);
80 | dest.writeInt(this.characteristicPermission);
81 | dest.writeSerializable(this.descriptorUuid);
82 | dest.writeInt(this.descriptorPermission);
83 | dest.writeByteArray(this.descriptorValue);
84 | }
85 |
86 | public ServiceProfile() {
87 | }
88 |
89 | protected ServiceProfile(Parcel in) {
90 | this.characteristicUuid = (UUID) in.readSerializable();
91 | this.characteristicProperties = in.readInt();
92 | this.characteristicPermission = in.readInt();
93 | this.descriptorUuid = (UUID) in.readSerializable();
94 | this.descriptorPermission = in.readInt();
95 | this.descriptorValue = in.createByteArray();
96 | }
97 |
98 | public static final Creator CREATOR = new Creator() {
99 | @Override
100 | public ServiceProfile createFromParcel(Parcel source) {
101 | return new ServiceProfile(source);
102 | }
103 |
104 | @Override
105 | public ServiceProfile[] newArray(int size) {
106 | return new ServiceProfile[size];
107 | }
108 | };
109 |
110 | @Override
111 | public String toString() {
112 | return "ServiceProfile{" +
113 | "characteristicUuid=" + characteristicUuid +
114 | ", characteristicProperties=" + characteristicProperties +
115 | ", characteristicPermission=" + characteristicPermission +
116 | ", descriptorUuid=" + descriptorUuid +
117 | ", descriptorPermission=" + descriptorPermission +
118 | ", descriptorValue=" + Arrays.toString(descriptorValue) +
119 | '}';
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/library/src/main/java/com/davistsin/bluetoothle/replicas/ServiceSettings.java:
--------------------------------------------------------------------------------
1 | package com.davistsin.bluetoothle.replicas;
2 |
3 | import java.util.List;
4 | import java.util.UUID;
5 |
6 | /**
7 | * Created by David Qin on 2017/4/19.
8 | */
9 |
10 | public final class ServiceSettings {
11 | UUID serviceUuid;
12 | int serviceType;
13 | List serviceProfiles;
14 |
15 | private ServiceSettings(ServiceSettings.Builder builder) {
16 | this.serviceUuid = builder.serviceUuid;
17 | this.serviceType = builder.serviceType;
18 | serviceProfiles = builder.serviceProfiles;
19 | }
20 |
21 | public static final class Builder {
22 | private UUID serviceUuid;
23 | private int serviceType;
24 | private List serviceProfiles;
25 |
26 | public Builder setServiceUuid(UUID serviceUuid) {
27 | this.serviceUuid = serviceUuid;
28 | return this;
29 | }
30 |
31 | public Builder setServiceType(int serviceType) {
32 | this.serviceType = serviceType;
33 | return this;
34 | }
35 |
36 | public Builder addServiceProfiles(List serviceProfiles) {
37 | this.serviceProfiles = serviceProfiles;
38 | return this;
39 | }
40 |
41 | public ServiceSettings build() {
42 | return new ServiceSettings(this);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BluetoothLeLibrary
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'BluetoothLELibrary'
2 | include ':app', ':library'
3 |
--------------------------------------------------------------------------------