├── .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 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 33 | 53 | 54 | 55 | 56 | 57 | 58 | 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 | ![title](./image/title.jpg) 2 | 3 | [![](https://jitpack.io/v/davistsin/BluetoothLELibrary.svg)](https://jitpack.io/#davistsin/BluetoothLELibrary) ![license](https://img.shields.io/github/license/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 | ![title](https://github.com/qindachang/BluetoothLELibrary/blob/master/image/title.jpg) 3 | 4 | ![JitPack.io](https://img.shields.io/pypi/l/Django.svg) 5 | ![Release Version](https://img.shields.io/badge/release-0.5.6-red.svg) 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 | --------------------------------------------------------------------------------