├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── layout
│ │ │ ├── item_device.xml
│ │ │ ├── activity_main.xml
│ │ │ └── activity_ble.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── kongzue
│ │ └── btlinker
│ │ ├── MainActivity.java
│ │ └── BLEActivity.java
├── proguard-rules.pro
└── build.gradle
├── btutil
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── com
│ │ │ └── kongzue
│ │ │ └── btutil
│ │ │ ├── interfaces
│ │ │ ├── OnBLEReadListener.java
│ │ │ ├── OnBLENotificationListener.java
│ │ │ ├── OnBLEWriteListener.java
│ │ │ ├── OnBtSocketResponseListener.java
│ │ │ ├── OnDeviceLinkStatusChangeListener.java
│ │ │ ├── BluetoothOpenListener.java
│ │ │ ├── BtLinkReport.java
│ │ │ ├── OnLinkStatusChangeListener.java
│ │ │ ├── OnBLEFindServiceListener.java
│ │ │ └── OnBLEScanListener.java
│ │ │ ├── receiver
│ │ │ └── BluetoothConnectActivityReceiver.java
│ │ │ ├── util
│ │ │ ├── TaskExecutor.java
│ │ │ └── GattAttributes.java
│ │ │ ├── SPPLinkUtil.java
│ │ │ └── BLELinkUtil.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── dictionaries
│ └── myzcx.xml
├── encodings.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── runConfigurations.xml
├── gradle.xml
└── misc.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/btutil/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':btutil'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BTLinker
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/dictionaries/myzcx.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/btutil/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BTUtil
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kongzue/BTLinker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #008577
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBLEReadListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2019/1/9 20:48
9 | */
10 | public interface OnBLEReadListener {
11 | void onReadMessage(String msg);
12 | }
13 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBLENotificationListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2019/1/9 23:27
9 | */
10 | public interface OnBLENotificationListener {
11 | void onGetData(String data);
12 | }
13 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBLEWriteListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2019/1/9 21:15
9 | */
10 | public interface OnBLEWriteListener {
11 | boolean onWrite(boolean isSuccess,String returnMsg);
12 | }
13 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBtSocketResponseListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2018/12/11 17:22
9 | */
10 | public interface OnBtSocketResponseListener {
11 |
12 | void onResponse(String msg);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnDeviceLinkStatusChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2019/4/3 16:08
9 | */
10 | public interface OnDeviceLinkStatusChangeListener {
11 | void onLinked();
12 |
13 | void onLinkFailed();
14 | }
15 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/BluetoothOpenListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2019/3/27 16:36
9 | */
10 | public interface BluetoothOpenListener {
11 |
12 | void onResponse(boolean isSuccess,int errorCode,String errorMsg);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/BtLinkReport.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2018/9/2 16:44
9 | */
10 | public interface BtLinkReport {
11 | void onStatusChange(String message);
12 |
13 | void onError(String message);
14 |
15 | void onGetData(String datas);
16 | }
17 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnLinkStatusChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | /**
4 | * Author: @Kongzue
5 | * Github: https://github.com/kongzue/
6 | * Homepage: http://kongzue.com/
7 | * Mail: myzcxhh@live.cn
8 | * CreateTime: 2018/12/11 17:22
9 | */
10 | public interface OnLinkStatusChangeListener {
11 |
12 | void onStartLink();
13 |
14 | void onSuccess();
15 |
16 | void onFailed(int errorCode);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBLEFindServiceListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | import android.bluetooth.BluetoothGattService;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Author: @Kongzue
9 | * Github: https://github.com/kongzue/
10 | * Homepage: http://kongzue.com/
11 | * Mail: myzcxhh@live.cn
12 | * CreateTime: 2019/1/9 20:38
13 | */
14 | public interface OnBLEFindServiceListener {
15 |
16 | void onLink(boolean isSuccess, List services);
17 | }
18 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/btutil/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/interfaces/OnBLEScanListener.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.interfaces;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.widget.ListView;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Author: @Kongzue
11 | * Github: https://github.com/kongzue/
12 | * Homepage: http://kongzue.com/
13 | * Mail: myzcxhh@live.cn
14 | * CreateTime: 2019/1/8 21:38
15 | */
16 | public interface OnBLEScanListener {
17 | BluetoothDevice onFindDevice(BluetoothDevice device);
18 |
19 | void getAllDevice(List devices);
20 |
21 | void onStop();
22 | }
23 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/btutil/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.kongzue.btlinker"
7 | minSdkVersion 19
8 | targetSdkVersion 28
9 | versionCode 2
10 | versionName "2.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation fileTree(include: ['*.jar'], dir: 'libs')
22 | implementation 'com.android.support:appcompat-v7:28.0.0'
23 | implementation 'com.kongzue.baseframework:baseframework:6.6.0'
24 | implementation project(':btutil')
25 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
23 |
24 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/receiver/BluetoothConnectActivityReceiver.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.receiver;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 |
8 | import com.kongzue.btutil.SPPLinkUtil;
9 |
10 | /**
11 | * Author: @Kongzue
12 | * Github: https://github.com/kongzue/
13 | * Homepage: http://kongzue.com/
14 | * Mail: myzcxhh@live.cn
15 | * CreateTime: 2018/9/2 23:01
16 | */
17 | public class BluetoothConnectActivityReceiver extends BroadcastReceiver {
18 |
19 | @Override
20 | public void onReceive(Context context, Intent intent) {
21 | if (intent.getAction().equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
22 | BluetoothDevice mBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
23 | try {
24 | //(三星)4.3版本测试手机还是会弹出用户交互页面(闪一下),如果不注释掉下面这句页面不会取消但可以配对成功。(中兴,魅族4(Flyme 6))5.1版本手机两中情况下都正常
25 | //ClsUtils.setPairingConfirmation(mBluetoothDevice.getClass(), mBluetoothDevice, true);
26 | abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
27 | //3.调用setPin方法进行配对...
28 | boolean ret = SPPLinkUtil.setPin(mBluetoothDevice.getClass(), mBluetoothDevice);
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | }
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/btutil/src/main/java/com/kongzue/btutil/util/TaskExecutor.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btutil.util;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 |
7 | import java.util.concurrent.Executor;
8 |
9 | import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
10 |
11 | /**
12 | * Created by LiuLei on 16/10/28.
13 | * Github: https://github.com/Alex-Jerry/Android-BLE
14 | */
15 | public class TaskExecutor {
16 |
17 | private static Executor mParallelExecutor = AsyncTask.THREAD_POOL_EXECUTOR;//最多为5个线程 可以并发执行(也就是说最多只有5个线程同时运行,超过5个的就要等待) 异步线程池
18 | private static Executor mSerialExecutor = AsyncTask.SERIAL_EXECUTOR;//按照顺序执行 同步线程池(系统默认使用的)
19 | private static Handler sHandler = new Handler(Looper.getMainLooper());
20 |
21 | private TaskExecutor() {
22 | mParallelExecutor = THREAD_POOL_EXECUTOR;
23 | mSerialExecutor = AsyncTask.SERIAL_EXECUTOR;
24 | }
25 |
26 | public static void runOnUIThread(Runnable runnable) {
27 | sHandler.post(runnable);
28 | }
29 |
30 | public static void executeTask(Runnable task) {
31 | executeTask(task, true);
32 | }
33 |
34 | public static void executeTaskSerially(Runnable task) {
35 | executeTask(task, false);
36 | }
37 |
38 | public static void executeTask(Runnable task, boolean parallel) {
39 | if (parallel) {
40 | mParallelExecutor.execute(task);
41 | return;
42 | }
43 | mSerialExecutor.execute(task);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/btutil/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 |
5 | def siteUrl = 'https://github.com/kongzue/BTLinker' //项目在github主页地址
6 | def gitUrl = 'https://github.com/kongzue/BTLinker.git' //Git仓库的地址
7 |
8 | group = "com.kongzue.smart"//发布aar前缀根节点
9 | version = "1.0.8"//发布aar的库版本
10 |
11 | android {
12 | compileSdkVersion 28
13 |
14 | defaultConfig {
15 | minSdkVersion 19
16 | targetSdkVersion 28
17 | versionCode 17
18 | versionName "1.0.8"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | lintOptions {
29 | abortOnError false
30 | }
31 | }
32 | install {
33 | repositories.mavenInstaller {
34 | // This generates POM.xml with proper parameters
35 | pom {
36 | project {
37 | packaging 'aar'
38 | name 'btlinker'//添加项目描述
39 | url siteUrl
40 | licenses {
41 | license {
42 | name 'The Apache Software License, Version 2.0'
43 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
44 | }
45 | }
46 | developers {
47 | developer {
48 | id 'myzchh'//设置自己ID
49 | name 'myzchh'//设置自己名字
50 | email 'myzcxhh@live.cn'//设置自己邮箱
51 | }
52 | }
53 | scm {
54 | connection gitUrl
55 | developerConnection gitUrl
56 | url siteUrl
57 | }
58 | }
59 | }
60 | }
61 | }
62 | task sourcesJar(type: Jar) {
63 | from android.sourceSets.main.java.srcDirs
64 | classifier = 'sources'
65 | }
66 |
67 | artifacts {
68 | archives sourcesJar
69 | }
70 |
71 | Properties properties = new Properties()
72 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
73 | bintray {
74 | user = properties.getProperty("bintray.user")
75 | key = properties.getProperty("bintray.apikey")
76 | configurations = ['archives']
77 | pkg {
78 | repo = "maven"
79 | name = "BTLinker" //项目在JCenter的名字
80 | websiteUrl = siteUrl
81 | vcsUrl = gitUrl
82 | licenses = ["Apache-2.0"]
83 | publish = true
84 | }
85 | }
86 | dependencies {
87 | implementation fileTree(dir: 'libs', include: ['*.jar'])
88 |
89 | implementation 'com.android.support:appcompat-v7:28.0.0'
90 | }
91 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
30 |
31 |
35 |
36 |
41 |
42 |
46 |
47 |
52 |
53 |
58 |
59 |
60 |
67 |
68 |
72 |
73 |
77 |
78 |
83 |
84 |
88 |
89 |
94 |
95 |
99 |
100 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_ble.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
22 |
23 |
32 |
33 |
39 |
40 |
45 |
46 |
50 |
51 |
52 |
53 |
60 |
61 |
65 |
66 |
67 |
68 |
75 |
76 |
83 |
84 |
88 |
89 |
93 |
94 |
98 |
99 |
103 |
104 |
108 |
109 |
114 |
115 |
120 |
121 |
126 |
127 |
128 |
129 |
134 |
135 |
140 |
141 |
146 |
147 |
148 |
149 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kongzue/btlinker/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btlinker;
2 |
3 | import android.Manifest;
4 | import android.app.ProgressDialog;
5 | import android.view.View;
6 | import android.widget.AdapterView;
7 | import android.widget.ArrayAdapter;
8 | import android.widget.Button;
9 | import android.widget.EditText;
10 | import android.widget.LinearLayout;
11 | import android.widget.Spinner;
12 | import android.widget.TextView;
13 |
14 | import com.kongzue.baseframework.BaseActivity;
15 | import com.kongzue.baseframework.interfaces.Layout;
16 | import com.kongzue.baseframework.util.JumpParameter;
17 | import com.kongzue.baseframework.util.OnPermissionResponseListener;
18 | import com.kongzue.btutil.SPPLinkUtil;
19 | import com.kongzue.btutil.interfaces.OnBtSocketResponseListener;
20 | import com.kongzue.btutil.interfaces.OnLinkStatusChangeListener;
21 |
22 | import static com.kongzue.btutil.SPPLinkUtil.ERROR_BREAK;
23 |
24 | @Layout(R.layout.activity_main)
25 | public class MainActivity extends BaseActivity {
26 |
27 | private SPPLinkUtil SPPLinkUtil;
28 |
29 | private String[] arraysStr = {"\\r\\n", "\\n\\r", "\\n", "\\r"};
30 | private String[] arrays = {"\r\n", "\n\r", "\n", "\r"};
31 | private String enterStr = "\r\n";
32 |
33 | private EditText editUuid;
34 | private EditText editName;
35 | private Button btnLink;
36 | private LinearLayout boxConnected;
37 | private EditText editSend;
38 | private Spinner spEnter;
39 | private Button btnSend;
40 | private TextView txtGetMsg;
41 |
42 | @Override
43 | public void initViews() {
44 | editUuid = findViewById(R.id.edit_uuid);
45 | editName = findViewById(R.id.edit_name);
46 | btnLink = findViewById(R.id.btn_link);
47 | boxConnected = findViewById(R.id.box_connected);
48 | editSend = findViewById(R.id.edit_send);
49 | spEnter = findViewById(R.id.sp_enter);
50 | btnSend = findViewById(R.id.btn_send);
51 | txtGetMsg = findViewById(R.id.txt_getMsg);
52 | }
53 |
54 | @Override
55 | public void initDatas(JumpParameter paramer) {
56 | requestPermission(new String[]{
57 | Manifest.permission.ACCESS_COARSE_LOCATION,
58 | Manifest.permission.ACCESS_FINE_LOCATION
59 | }, new OnPermissionResponseListener() {
60 | @Override
61 | public void onSuccess(String[] permissions) {
62 |
63 | }
64 |
65 | @Override
66 | public void onFail() {
67 | toast("请允许权限");
68 | finish();
69 | }
70 | });
71 |
72 | ArrayAdapter adapter = new ArrayAdapter(
73 | MainActivity.this, android.R.layout.simple_spinner_item,
74 | arraysStr
75 | );
76 |
77 | spEnter.setAdapter(adapter);
78 |
79 | SPPLinkUtil.DEBUGMODE = true;
80 | SPPLinkUtil = new SPPLinkUtil();
81 |
82 | }
83 |
84 | private ProgressDialog progressDialog;
85 |
86 | @Override
87 | public void setEvents() {
88 | spEnter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
89 | @Override
90 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
91 | enterStr = arrays[position];
92 | }
93 |
94 | @Override
95 | public void onNothingSelected(AdapterView> parent) {
96 |
97 | }
98 | });
99 |
100 | btnLink.setOnClickListener(new View.OnClickListener() {
101 | @Override
102 | public void onClick(View v) {
103 | String name = editName.getText().toString().trim();
104 | String uuid = editUuid.getText().toString().trim();
105 | String pairingCode = "1234";
106 |
107 | if (isNull(name)) {
108 | toast("蓝牙名称不能为空");
109 | return;
110 | }
111 | SPPLinkUtil.DEBUGMODE = true;
112 | SPPLinkUtil.setUUID(uuid)
113 | .setOnLinkStatusChangeListener(new OnLinkStatusChangeListener() {
114 | @Override
115 | public void onStartLink() {
116 | progressDialog = ProgressDialog.show(MainActivity.this, "连接中", "请稍候...");
117 | }
118 |
119 | @Override
120 | public void onSuccess() {
121 | progressDialog.dismiss();
122 | editName.setEnabled(false);
123 | editUuid.setEnabled(false);
124 | btnLink.setEnabled(false);
125 | boxConnected.setVisibility(View.VISIBLE);
126 | }
127 |
128 | @Override
129 | public void onFailed(final int errorCode) {
130 | progressDialog.dismiss();
131 | toast("错误:" + errorCode);
132 |
133 | if (errorCode == ERROR_BREAK) {
134 | toast("连接中断");
135 | editName.setEnabled(true);
136 | editUuid.setEnabled(true);
137 | btnLink.setEnabled(true);
138 | boxConnected.setVisibility(View.GONE);
139 | }
140 | }
141 | })
142 | .setOnBtSocketResponseListener(new OnBtSocketResponseListener() {
143 | @Override
144 | public void onResponse(String msg) {
145 | log("msg:"+msg);
146 | txtGetMsg.setText(msg);
147 | }
148 | });
149 |
150 | SPPLinkUtil.link(me, name);
151 | }
152 | });
153 |
154 | btnSend.setOnClickListener(new View.OnClickListener() {
155 | @Override
156 | public void onClick(View v) {
157 | String msg = editSend.getText().toString().trim();
158 | if (isNull(msg)) {
159 | toast("请输入指令");
160 | return;
161 | }
162 | SPPLinkUtil.send(msg + enterStr);
163 | }
164 | });
165 |
166 | }
167 |
168 | @Override
169 | protected void onDestroy() {
170 | SPPLinkUtil.close(me);
171 | super.onDestroy();
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BTLinker
2 | 蓝牙连接封装库,适用于智能硬件蓝牙通讯,使用 SPP 服务以及 BLE。
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Demo预览图如下:
18 |
19 | 
20 |
21 | Demo下载:
22 |
23 | ## ⚠前言
24 |
25 | 目前已支持 SPP 和 BLE 模式,对应使用 SPPLinkUtil 和 BLELinkUtil 工具类。
26 |
27 | 本框架无需做太多复杂操作,开关蓝牙也都是自动的,您只需要连接需要的设备,进行相关的操作即可。
28 |
29 | 使用本框架需要一定的蓝牙结构知识,对于 SPP 和 BLE 通讯的结构组成,本文不再赘述,要了解请自行搜索资料。
30 |
31 | ### 关于蓝牙2.0(SPP)的一些说明
32 |
33 | 因蓝牙 Socket 存在天坑,数据包可能发生粘包情况,请您与硬件端约定一个消息结束符(默认为各种回车符) 为结尾(默认\r\n),来代表此条消息结束,否则软件层面上无法得知消息结束会继续等待后续的消息导致无法通过监听器吐出任何数据。
34 |
35 | 偶尔发送数据包会存在丢包的风险导致硬件端未接受到指令,建议发送指令后服务端返回个约定的执行成功指令给客户端,若没有建议客户端重复发送之前的指令直到成功。
36 |
37 | ### 关于蓝牙4.0(BLE)的一些说明
38 |
39 | 由于 Android 底层限制,默认发送、接收的消息内容都被限制在20字节以内,导致收发消息出现断断续续或只有前20字的情况,本框架会在完成通道连接后发送最大512字节的数据包申请,若无效,请与硬件开发联系支持更大的数据包。
40 |
41 | 要发送20字以上数据请使用 writeBIG(...) 方法,该方法的实现原理是将要发送的数据裁剪为多个20字节的数据包,每隔50毫秒发送一次,请与硬件端商量进行数据组包。
42 |
43 | 由于 BLE 接收的消息可能存在粘包情况(上下两条消息发生断续或首尾相接的问题),为保证读取到的消息完整性,建议与设备端开发约定消息头及消息尾的字符,默认以“$”开头、以“\r\n”结尾为一条消息,可通过阅读详细文档设置修改,如果未读取到消息尾,回调方法可能发生不执行情况,请知悉。
44 |
45 | ## 使用方法
46 | 1) 从 Maven 仓库或 jCenter 引入:
47 | Maven仓库:
48 | ```
49 |
50 | com.kongzue.smart
51 | btutil
52 | 1.0.8
53 | pom
54 |
55 | ```
56 | Gradle:
57 | 在dependencies{}中添加引用:
58 | ```
59 | implementation 'com.kongzue.smart:btutil:1.0.8'
60 | ```
61 |
62 | ## 关于权限
63 | 您需要申请蓝牙权限后才可以正常使用
64 |
65 | 主要权限:
66 | ```
67 |
68 |
69 |
70 |
71 | ```
72 |
73 | 需要申请:
74 | ```
75 | Manifest.permission.ACCESS_COARSE_LOCATION,
76 | Manifest.permission.ACCESS_FINE_LOCATION
77 | ```
78 |
79 | 若不声明或申请权限,可能导致无法查找到要连接的目标蓝牙,或无法正常使用功能。
80 |
81 | ### 蓝牙 2.0 SPP
82 |
83 | 1) 初始化 SPPLinkUtil
84 | ```
85 | private SPPLinkUtil spplinkUtil;
86 | //...
87 | SPPLinkUtil = new SPPLinkUtil();
88 |
89 | //UUID 是 SPP 服务的 UUID号,如果使用 HC-06 蓝牙主板则无需修改。
90 | SPPLinkUtil.setUUID(uuid)
91 |
92 | //设置状态监听
93 | .setOnLinkStatusChangeListener(new OnLinkStatusChangeListener() {
94 | @Override
95 | public void onStartLink() {
96 | //开始连接
97 | }
98 |
99 | @Override
100 | public void onSuccess() {
101 | //连接完成
102 | }
103 |
104 | @Override
105 | public void onFailed(int errorCode) {
106 | //连接错误,详见《连接错误代码》章节
107 | }
108 | })
109 |
110 | //设置设备回传数据监听
111 | .setOnBtSocketResponseListener(new OnBtSocketResponseListener() {
112 | @Override
113 | public void onResponse(String msg) {
114 | //msg即设备返回App的数据
115 | }
116 | });
117 |
118 | //开始连接,若 context 是一个 Activity 则回调监听器会自动返回主线程操作。
119 | spplinkUtil.link(context, 蓝牙名称);
120 | ```
121 |
122 | 2) 其他方法
123 | ```
124 | //发送消息给设备
125 | spplinkUtil.send(String);
126 |
127 | //别忘记在 Activity 退出后结束事务
128 | @Override
129 | protected void onDestroy() {
130 | spplinkUtil.close(me);
131 | super.onDestroy();
132 | }
133 |
134 | //开启日志打印(请注意大小写)
135 | SPPLinkUtil.DEBUGMODE = true;
136 |
137 | //修改配对码(请注意大小写)
138 | SPPLinkUtil.setBtPairingCode("666666");
139 | ```
140 |
141 | 3) 连接错误代码
142 |
143 | 字段 | 值 | 含义
144 | ---|---|---
145 | ERROR_NO_DEVICE | -1 | 附近无设备
146 | ERROR_START_BT | -2 | 无法启动蓝牙
147 | ERROR_NOT_FOUND_DEVICE | -3 | 未找到目标设备
148 | ERROR_NOT_CONNECTED | -4 | 未建立连接
149 | ERROR_SOCKET_ERROR | -70 | Socket故障
150 | ERROR_BREAK | -50 | 连接中断
151 |
152 | 4) 回传终止符(重要)
153 |
154 | 请与您的硬件开发者商定一个回传终止符。
155 |
156 | 回传终止符应当位于硬件设备回传消息的末尾,它用于标记此次指令结束,因传输方式为 socket,鉴于 socket 粘包的特性,若不设置回传终止符则无法确定此次消息结束。
157 |
158 | 默认的回传接收终止符为“\n”或“\r”或“\r\n”或“\n\r”,也就是说,如果硬件向 App 发送的消息末尾没有换行,则 SPPLinkUtil 不会认为本条消息结束,继续处于等待状态。
159 |
160 | 只有接收到回传终止符,OnBtSocketResponseListener 中的回调才会有效,将之前缓存的消息传送出来。
161 |
162 | ### 蓝牙 4.0 BLE
163 | >初始化BLE组件
164 | ```
165 | bleLinkUtil = new BLELinkUtil();
166 | ```
167 |
168 | 使用步骤一般为 **开启蓝牙 → 扫描并连接设备 → 连接成功后设置服务通道扫描监听器回调**
169 |
170 | >开启蓝牙
171 | ```
172 | bleLinkUtil.openBluetooth(new BluetoothOpenListener() {
173 | @Override
174 | public void onResponse(boolean isSuccess, int errorCode, String errorMsg) {
175 |
176 | }
177 | }
178 | ```
179 |
180 | >初始化后就需要寻找附近的BLE设备了:
181 | ```
182 | //设置查询监听(寻找附近的BLE设备)
183 | bleLinkUtil.doScan(new OnBLEScanListener() {
184 | @Override
185 | public BluetoothDevice onFindDevice(BluetoothDevice device) {
186 |
187 | }
188 |
189 | @Override
190 | public void getAllDevice(List devices) {
191 |
192 | }
193 |
194 | @Override
195 | public void onStop() {
196 |
197 | }
198 | );
199 | ```
200 | >设置监听器 setOnBLEScanListener,其中有两个接口,一个是 getAllDevice(List devices) 会重复性的返回所有当前已找到的设备列表,适用于以此制作 Adapter 来显示 ListView 列表。
201 |
202 | 另一个方法 onFindDevice(BluetoothDevice device) 会在每查找到一个新设备时返回,不会重复,可以进行判断后直接 return 该设备直接连接该设备,适用于快速完成设备连接的业务流程。
203 |
204 | 注:直接 return 方式连接前需要手动设置 bleLinkUtil.setOnBLEFindServiceListener(...) 连接状态回调监听器。
205 |
206 | >另外要手动进行设备连接,可以使用以下方式:
207 | ```
208 | bleLinkUtil.linkDevice(devices.get(position), new OnBLEFindServiceListener() {
209 | @Override
210 | public void onLink(boolean isSuccess, final List services) {
211 | //对 services 进行处理,注意此时为异步线程。
212 | }
213 | });
214 | ```
215 | >如果已知要连接的 BLE 设备的 Mac 地址,可以使用以下方式,注意此时不需要上述扫描附近 BLE 设备的过程:
216 | ```
217 | bleLinkUtil.linkDevice(macAddress, new OnBLEFindServiceListener() {
218 | @Override
219 | public void onLink(boolean isSuccess, final List services) {
220 | //对 services 进行处理,注意此时为异步线程。
221 | }
222 | });
223 | ```
224 | 返回的 List services 为连接成功后该 BLE 设备提供的服务,可以通过 services.getUuid() 获取其 UUID,可以通过 services.getCharacteristics() 获取其通道。
225 |
226 | 获取到的 Characteristics 对象,可使用 characteristics.getUuid() 判断其 UUID,判断是否为自己需要的通道。
227 |
228 | 对于已知 Service 和 Characteristics 的 UUID 的情况下,也可通过以下方法直接获取通道:
229 | ```
230 | BluetoothGattCharacteristic characteristic = bleLinkUtil.getCharacteristic(serviceUUID, childUUID);
231 | ```
232 |
233 | 也可以直接设置接下来要操作的通道:
234 | ```
235 | bleLinkUtil.setUUID(serviceUUID, childUUID);
236 | ```
237 |
238 | >获取到通道后,就可以进行主要的操作了:
239 |
240 | 1) 进行消息读取:
241 | ```
242 | bleLinkUtil.read(new OnBLEReadListener() {
243 | @Override
244 | public void onReadMessage(String msg) {
245 |
246 | }
247 | });
248 | ```
249 |
250 | 2) 进行消息的写入:
251 | ```
252 | String text = editWrite.getText().toString().trim();
253 | if (!text.isEmpty()) {
254 | bleLinkUtil.write(text, new OnBLEWriteListener() {
255 | @Override
256 | public boolean onWrite(boolean isSuccess,String returnMsg) {
257 | if (isSuccess) {
258 | Toast.makeText(me, "发送成功", Toast.LENGTH_SHORT).show();
259 | } else {
260 | Toast.makeText(me, "发送失败", Toast.LENGTH_SHORT).show();
261 | }
262 | return false;
263 | }
264 | });
265 | }
266 | ```
267 |
268 | OnBLEWriteListener 中回调参数 returnMsg 是在发送指令后,通过通道的 read 方式读取到的值,若该通道启用了通知,那么 OnBLEWriteListener 会被多次回调,且通知消息也会在 returnMsg 中回传,直到您执行 return true; 来代表您已处理回传的消息。
269 |
270 | 之所以这么做,原因是部分硬件开发习惯性通过“通知”来回传指令消息,但通知是不断接收的,同时还会有其他通知回传,因此需要不停的判断是否读取到回传的“通知”成功后通过 return true 来停止判断。
271 |
272 | 大于20字节消息的写入:
273 | ```
274 | String text = editWrite.getText().toString().trim();
275 | if (!text.isEmpty()) {
276 | bleLinkUtil.writeBIG(text, new OnBLEWriteListener() {
277 | @Override
278 | public boolean onWrite(boolean isSuccess,String returnMsg) {
279 | if (isSuccess) {
280 | Toast.makeText(me, "发送成功", Toast.LENGTH_SHORT).show();
281 | } else {
282 | Toast.makeText(me, "发送失败", Toast.LENGTH_SHORT).show();
283 | }
284 | return false;
285 | }
286 | });
287 | }
288 | ```
289 |
290 | 3) 进行通知消息的订阅:
291 |
292 | 所谓通知消息,即申请订阅该通知后,设备端会不断通过蓝牙给手机端传送数据。
293 | ```
294 | bleLinkUtil.startGetNotification(new OnBLENotificationListener() {
295 | @Override
296 | public void onGetData(String data) {
297 |
298 | }
299 | });
300 | ```
301 |
302 | 额外的,可以通过以下代码订阅其他通道的通知:
303 | ```
304 | //SERVICE_UUID 为服务的UUID,NOTIFY_CHARACTERISTIC_UUID 为指定通知管道的 UUID
305 | bleLinkUtil.startGetNotification(SERVICE_UUID, NOTIFY_CHARACTERISTIC_UUID, new OnBLENotificationListener() {
306 | @Override
307 | public void onGetData(String data) {
308 | log("接收到通知: " + data);
309 | }
310 | });
311 | ```
312 |
313 | 4) 额外方法
314 |
315 | >设置消息头:
316 | ```
317 | BleLinkUtil.messageStart = "$";
318 | ```
319 |
320 | >设置消息尾:
321 | ```
322 | BleLinkUtil.messageEnd = "\r\n";
323 | ```
324 |
325 | >停止搜寻设备:
326 | ```
327 | bleLinkUtil.stopScan();
328 | ```
329 |
330 | >结束事务(包括停止一切活动) **⚠ 建议放在 Activity 的 onDestroy() 事件中执行**
331 | ```
332 | bleLinkUtil.cancel();
333 | ```
334 |
335 | >判断通道属性:
336 | ```
337 | //判断通道可读
338 | bleLinkUtil.ifCharacteristicReadable(Characteristic);
339 |
340 | //判断通道可写
341 | bleLinkUtil.ifCharacteristicWritable(Characteristic);
342 |
343 | //判断通道是否通知
344 | bleLinkUtil.ifCharacteristicNotifiable(Characteristic);
345 |
346 | //是否开启 DEBUG 模式(开启后本工具会持续打印日志信息用于辅助判断流程是否存在问题)
347 | bleLinkUtil.DEBUGMODE = true;
348 |
349 | ```
350 |
351 | ## 开源协议
352 | ```
353 | Copyright Kongzue BTLinker
354 |
355 | Licensed under the Apache License, Version 2.0 (the "License");
356 | you may not use this file except in compliance with the License.
357 | You may obtain a copy of the License at
358 |
359 | http://www.apache.org/licenses/LICENSE-2.0
360 |
361 | Unless required by applicable law or agreed to in writing, software
362 | distributed under the License is distributed on an "AS IS" BASIS,
363 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
364 | See the License for the specific language governing permissions and
365 | limitations under the License.
366 | ```
367 |
368 | ## 更新日志
369 | v1.0.8:
370 | - linkDevice(mac, listener) 新增使用 mac 地址连接设备;
371 |
372 | 更新提示:因收到反馈,MIUI 在最新版本推送中修改系统逻辑,导致 BLE 蓝牙 Scan 查询附近蓝牙的过程无效(猜测是为屏蔽“探针盒子”),但也会导致之前的“查找”→“连接”的方式无效,因此本次更新新增通过 Mac 地址直接连接指定设备的方式,此方式已验证目前可用。
373 |
374 | v1.0.7:
375 | - 完善和修复 bug;
376 |
377 | v1.0.6.1:
378 | - 新增 BLE 工具类消息头、消息尾设置;
379 |
380 | v1.0.6.0:
381 | - 更新 BLE 工具类并修复了一些 bug;
382 |
383 | v1.0.5:
384 | - 修复了一些连接逻辑 bug;
385 |
386 | v1.0.4:
387 | - 解决蓝牙 4.0(BLE)传输数据20字节限制的问题。
388 |
389 | v1.0.3:
390 | - 修复了一些bug;
391 |
392 | v1.0.2:
393 | - 修改逻辑,并将目前 LinkUtil 改名为 SPPLinkUtil;
394 | - 修复了一些bug;
395 |
396 | v1.0.1:
397 | - 新增连接后的断开校验;
398 | - 修改建立连接逻辑,连接更稳定;
399 |
400 | v1.0.0:
401 | - 首次上传;
402 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kongzue/btlinker/BLEActivity.java:
--------------------------------------------------------------------------------
1 | package com.kongzue.btlinker;
2 |
3 | import android.app.ProgressDialog;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothGattCharacteristic;
7 | import android.bluetooth.BluetoothGattService;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.AdapterView;
13 | import android.widget.Button;
14 | import android.widget.EditText;
15 | import android.widget.LinearLayout;
16 | import android.widget.ListView;
17 | import android.widget.SimpleAdapter;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import com.kongzue.btutil.BLELinkUtil;
22 | import com.kongzue.btutil.interfaces.OnBLEFindServiceListener;
23 | import com.kongzue.btutil.interfaces.OnBLENotificationListener;
24 | import com.kongzue.btutil.interfaces.OnBLEReadListener;
25 | import com.kongzue.btutil.interfaces.OnBLEScanListener;
26 | import com.kongzue.btutil.interfaces.OnBLEWriteListener;
27 | import com.kongzue.btutil.util.GattAttributes;
28 |
29 | import java.util.ArrayList;
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 |
34 | public class BLEActivity extends AppCompatActivity {
35 |
36 | public static final String AUTO_LINK_DEVICE_MAC = "D8:B0:4C:CC:70:EF";
37 |
38 | private LinearLayout boxDevice;
39 | private Button btnLink;
40 | private ListView listDevice;
41 | private LinearLayout boxService;
42 | private ListView listService;
43 | private LinearLayout boxLinked;
44 | private TextView txtName;
45 | private TextView txtServiceUUID;
46 | private TextView txtChildUUID;
47 | private Button btnSelect;
48 | private Button btnRead;
49 | private Button btnNotify;
50 | private TextView txtRead;
51 | private LinearLayout boxWrite;
52 | private EditText editWrite;
53 | private Button btnWrite;
54 |
55 | private ProgressDialog progressDialog;
56 |
57 | private BLELinkUtil bleLinkUtil;
58 | private BLEActivity me;
59 | private List