├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.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
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_send_and_recive.xml
│ │ │ │ └── item_device_list.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── hansion
│ │ │ └── hble
│ │ │ └── sample
│ │ │ ├── adapter
│ │ │ ├── BaseViewHolder.java
│ │ │ └── DeviceListAdapter.java
│ │ │ ├── SendAndReciveActivity.java
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── hansion
│ │ │ └── hble
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── hansion
│ │ └── hble
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── h_ble
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── hansion
│ │ │ │ └── h_ble
│ │ │ │ ├── callback
│ │ │ │ ├── OnReceiverCallback.java
│ │ │ │ ├── ConnectCallback.java
│ │ │ │ ├── ScanCallback.java
│ │ │ │ ├── OnWriteCallback.java
│ │ │ │ └── BleDeviceScanCallback.java
│ │ │ │ ├── request
│ │ │ │ ├── IRequestQueue.java
│ │ │ │ └── ReceiverRequestQueue.java
│ │ │ │ └── BleController.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── hansion
│ │ │ └── h_ble
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── hansion
│ │ └── h_ble
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── copyright
│ └── profiles_settings.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── compiler.xml
└── misc.xml
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/h_ble/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':h_ble'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | hble
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/h_ble/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | h_ble
3 |
4 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hansionit/H-Ble/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/callback/OnReceiverCallback.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.callback;
2 |
3 | /**
4 | * 描述:接收设备向手机发送的广播数据
5 | */
6 | public interface OnReceiverCallback {
7 |
8 | void onRecive(byte[] value);
9 | }
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/request/IRequestQueue.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.request;
2 |
3 | /**
4 | * 描述:请求队列
5 | */
6 | public interface IRequestQueue {
7 |
8 | void set(String key, T t);
9 |
10 | T get(String key);
11 | }
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/callback/ConnectCallback.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.callback;
2 |
3 | /**
4 | * Description:
5 | * Author: Hansion
6 | * Time: 2016/10/11 12:11
7 | */
8 | public interface ConnectCallback {
9 | /**
10 | * Notify之后的回调
11 | */
12 | void onConnSuccess();
13 |
14 | void onConnFailed();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/hansion/hble/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/h_ble/src/test/java/com/hansion/h_ble/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/callback/ScanCallback.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.callback;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 |
5 | /**
6 | * Description:
7 | * Author: Hansion
8 | * Time: 2016/10/11 12:11
9 | */
10 | public interface ScanCallback {
11 | /**
12 | * 扫描完成,成功回调
13 | */
14 | void onSuccess();
15 |
16 | /**
17 | * 扫描过程中,每扫描到一个设备回调一次
18 | *
19 | * @param device 扫描到的设备
20 | * @param rssi 设备的信息强度
21 | * @param scanRecord
22 | */
23 | void onScanning(final BluetoothDevice device, int rssi, byte[] scanRecord);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/h_ble/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/callback/OnWriteCallback.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.callback;
2 |
3 | /**
4 | * 描述:写操作回调接口
5 | */
6 | public interface OnWriteCallback {
7 |
8 | /**
9 | * 蓝牙未开启
10 | */
11 | int FAILED_BLUETOOTH_DISABLE = 1;
12 | /**
13 | * 服务无效
14 | */
15 | int FAILED_INVALID_SERVICE = 2;
16 | /**
17 | * 特征无效
18 | */
19 | int FAILED_INVALID_CHARACTER = 3;
20 | /**
21 | * 操作失败
22 | */
23 | int FAILED_OPERATION = 5;
24 |
25 | /**
26 | * 写入成功
27 | */
28 | void onSuccess();
29 |
30 | /**
31 | * 写入失败
32 | *
33 | * @param state
34 | */
35 | void onFailed(int state);
36 | }
37 |
--------------------------------------------------------------------------------
/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 C:\Users\Hansion\AppData\Local\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 |
--------------------------------------------------------------------------------
/h_ble/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 C:\Users\Hansion\AppData\Local\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 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/callback/BleDeviceScanCallback.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.callback;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 |
6 | /**
7 | * Description: ble蓝牙设备扫描回调
8 | * Author: Hansion
9 | * Time: 2016/10/11 12:21
10 | */
11 | public class BleDeviceScanCallback implements BluetoothAdapter.LeScanCallback {
12 | private ScanCallback mScanCallback;
13 |
14 | public BleDeviceScanCallback(ScanCallback scanCallback) {
15 | this.mScanCallback = scanCallback;
16 | }
17 |
18 | @Override
19 | public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
20 | if (null != mScanCallback) {
21 | //每次扫描到设备会回调此方法,应该在此回调方法中去除重复并加入集合,用于列表展示
22 | mScanCallback.onScanning(device, rssi, scanRecord);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/hansion/hble/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.hansion.hble", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/h_ble/src/androidTest/java/com/hansion/h_ble/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.hansion.h_ble.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/h_ble/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile 'com.android.support:appcompat-v7:25.1.0'
30 | testCompile 'junit:junit:4.12'
31 | }
32 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.1"
6 | defaultConfig {
7 | applicationId "com.hansion.hble"
8 | minSdkVersion 19
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.1.0'
28 | testCompile 'junit:junit:4.12'
29 | compile project(':h_ble')
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_send_and_recive.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
18 |
19 |
24 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/request/ReceiverRequestQueue.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble.request;
2 |
3 | import android.util.Log;
4 |
5 | import com.hansion.h_ble.callback.OnReceiverCallback;
6 |
7 | import java.util.HashMap;
8 |
9 | /**
10 | * 描述:接收通知数据请求队列
11 | */
12 | public class ReceiverRequestQueue implements IRequestQueue {
13 | private static final String TAG = "ReceiverRequestQueue";
14 | HashMap map = new HashMap<>();
15 |
16 | @Override
17 | public void set(String key, OnReceiverCallback onReceiver) {
18 | map.put(key, onReceiver);
19 | }
20 |
21 | @Override
22 | public OnReceiverCallback get(String key) {
23 | return map.get(key);
24 | }
25 |
26 | public HashMap getMap() {
27 | return map;
28 | }
29 |
30 | /**
31 | * 移除一个元素
32 | *
33 | * @param key
34 | */
35 | public boolean removeRequest(String key) {
36 | Log.d(TAG, "ReceiverRequestQueue before:" + map.size());
37 | OnReceiverCallback onReceiverCallback = map.remove(key);
38 | Log.d(TAG, "ReceiverRequestQueue after:" + map.size());
39 | return null == onReceiverCallback;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_device_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
24 |
25 |
26 |
30 |
31 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hansion/hble/sample/adapter/BaseViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble.sample.adapter;
2 |
3 | import android.content.Context;
4 | import android.util.SparseArray;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | /**
10 | * 通用ViewHolder,避免每个Adapter都需要写ViewHolder
11 | */
12 | public class BaseViewHolder {
13 |
14 | private SparseArray mView;
15 | private int mPosition;
16 | private View mConvertView;
17 |
18 | public BaseViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
19 | mPosition = position;
20 | mView = new SparseArray();
21 | mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
22 | mConvertView.setTag(this);
23 | }
24 |
25 | public static BaseViewHolder getViewHolder(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
26 | if (convertView == null) {
27 | return new BaseViewHolder(context, parent, layoutId, position);
28 | } else {
29 | BaseViewHolder viewHolder = (BaseViewHolder) convertView.getTag();
30 | viewHolder.mPosition = position;
31 | return viewHolder;
32 | }
33 | }
34 |
35 | public View getConvertView() {
36 | return mConvertView;
37 | }
38 |
39 | /** 通过id获取控件 */
40 | public T getView(int viewId) {
41 | View view = mView.get(viewId);
42 | if (view == null) {
43 | view = mConvertView.findViewById(viewId);
44 | mView.put(viewId, view);
45 | }
46 | return (T) view;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hansion/hble/sample/adapter/DeviceListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble.sample.adapter;
2 |
3 | import android.bluetooth.BluetoothDevice;
4 | import android.content.Context;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.TextView;
9 |
10 | import com.hansion.hble.R;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | /**
16 | * Description:
17 | * Author: Hansion
18 | * Time: 2017/2/13 11:23
19 | */
20 | public class DeviceListAdapter extends BaseAdapter {
21 |
22 | private List bluetoothDevices = new ArrayList();
23 | private Context mContext;
24 |
25 | public DeviceListAdapter(Context context,List bluetoothDevices) {
26 | mContext = context;
27 | this.bluetoothDevices = bluetoothDevices;
28 | }
29 |
30 | @Override
31 | public int getCount() {
32 | return bluetoothDevices.size();
33 | }
34 |
35 | @Override
36 | public Object getItem(int i) {
37 | return null;
38 | }
39 |
40 | @Override
41 | public long getItemId(int i) {
42 | return 0;
43 | }
44 |
45 | @Override
46 | public View getView(int position, View convertView, ViewGroup viewGroup) {
47 | BaseViewHolder holder = BaseViewHolder.
48 | getViewHolder(mContext, convertView, viewGroup, R.layout.item_device_list, position);
49 | TextView name = holder.getView(R.id.mDeviceName);
50 | TextView address = holder.getView(R.id.mDeviceMacAddress);
51 | name.setText(bluetoothDevices.get(position).getName());
52 | address.setText(bluetoothDevices.get(position).getAddress());
53 | return holder.getConvertView();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # H-Ble
2 | Android Ble类库,基于回调,暴露搜索、连接、发送、接收、断开连接等接口,无需关心细节操作即可进行Ble通信。
3 |
4 |
5 | # API
6 |
7 | >
8 | > 具体使用请参考本项目提供的例子
9 | >
10 |
11 | ### 获取BleController实例并进行初始化(初始化只需执行一次)
12 |
13 | * BleController.getInstance().init(this);
14 |
15 |
16 |
17 | ### 搜所设备,获取设备列表
18 | * BleController.getInstance().scanBle(int time,ScanCallback scanCallbak);
19 |
20 |
21 |
22 | ### 连接设备
23 | * BleController.getInstance().connect(int time,String deviceAdress,ConnectCallback connectCallback);
24 |
25 |
26 | ### 发送数据
27 | * BleController.getInstance().writeBuffer(byte[] buf,OnWriteCallback onWriteCallback);
28 |
29 |
30 |
31 | ### 注册接收数据的监听
32 | * BleController.getInstance().registReciveListener(String requestKey,OnReceiverCallback onReceiveCallback);
33 |
34 |
35 | ### 注销接收数据的监听
36 | * BleController.getInstance().unregistReciveListener(String requestKey);
37 |
38 |
39 | ### 断开连接
40 | * BleController.getInstance().closeBleConn();
41 |
42 |
43 |
44 | ---
45 |
46 | # 必备操作
47 |
48 | ### 添加权限
49 |
50 |
51 |
52 |
53 |
54 |
55 | 定位权限必须添加,否则Android 5.0以上的手机搜索不到设备
56 |
57 |
58 | ### 指定UUID
59 |
60 | * 打开BleController.class
61 | * 修改以下成员变量的值为自己所要通信的BLE模块对应的UUID
62 |
63 | private static final String BLUETOOTH_S = "0000fff0-0000-1000-8000-00805f9b34fb";
64 | private static final String BLUETOOTH_NOTIFY_C = "0000fff7-0000-1000-8000-00805f9b34fb";
65 | private static final String BLUETOOTH_WRITE_C = "0000fff6-0000-1000-8000-00805f9b34fb";
66 |
67 | > 变量名最后一位字母:
68 | >
69 | > S 代表service的UUID;
70 | >
71 | > C 代表characteristic的UUID;
72 |
73 |
74 | 如果硬件没有提供UUID,可以下载Ble调试助手进行查看
75 | [http://download.csdn.net/detail/hansion3333/9753311](http://download.csdn.net/detail/hansion3333/9753311 "Ble串口助手")
76 |
77 | ---
78 |
79 | > 本库满足Android 4.3以上手机和BLE模块之间的连接与互发数据。
80 | >
81 | > 本库并未将常用Ble手表、心率计等设备的UUID加入其中自动匹配。所以有此需求可以使用其他支持的库。
82 | >
83 | > 个人博客:[http://www.hansion.win](http://www.hansion.win)
84 | >
85 | > CSDN: [http://blog.csdn.net/hansion3333](http://blog.csdn.net/hansion3333)
86 |
87 | * 注意:蓝牙的使用,与设备有莫大的关联,所以适配方面需要多加处理 此库在ZUK Z2 6.0/7.0,魅蓝note3 5.1,红米note3 5.0,一加7.0,华为7.0等手机上测试无问题,其他机型请自测
88 |
--------------------------------------------------------------------------------
/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 code instead of
83 | rem the _cmd.exe /c_ return code!
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 |
--------------------------------------------------------------------------------
/.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 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hansion/hble/sample/SendAndReciveActivity.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble.sample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.text.TextUtils;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 |
12 | import com.hansion.h_ble.BleController;
13 | import com.hansion.h_ble.callback.OnReceiverCallback;
14 | import com.hansion.h_ble.callback.OnWriteCallback;
15 | import com.hansion.hble.R;
16 |
17 | public class SendAndReciveActivity extends AppCompatActivity implements View.OnClickListener {
18 |
19 | private BleController mBleController;
20 | private Button mSendButton;
21 | private EditText mSendEdit;
22 | private TextView mReciveText;
23 | private StringBuffer mReciveString = new StringBuffer();
24 | public static final String REQUESTKEY_SENDANDRECIVEACTIVITY = "SendAndReciveActivity";
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_send_and_recive);
30 |
31 | initView();
32 |
33 | // TODO 在新的界面要获取实例,无需init
34 | mBleController = BleController.getInstance();
35 |
36 | // TODO 接收数据的监听
37 | mBleController.registReciveListener(REQUESTKEY_SENDANDRECIVEACTIVITY, new OnReceiverCallback() {
38 | @Override
39 | public void onRecive(byte[] value) {
40 | mReciveString.append(mBleController.bytesToHexString(value) + "\r\n");
41 | mReciveText.setText(mReciveString.toString());
42 | }
43 | });
44 | }
45 |
46 | private void initView() {
47 | mSendButton = (Button) findViewById(R.id.mSendButton);
48 | mSendEdit = (EditText) findViewById(R.id.mSendEdit);
49 | mReciveText = (TextView) findViewById(R.id.mReciveText);
50 |
51 | mSendButton.setOnClickListener(this);
52 | }
53 |
54 | @Override
55 | public void onClick(View view) {
56 | switch (view.getId()) {
57 | case R.id.mSendButton:
58 | String sendText = mSendEdit.getText().toString().trim();
59 | if (TextUtils.isEmpty(sendText)) {
60 | Toast.makeText(this, "请输入要发送的内容!", Toast.LENGTH_SHORT).show();
61 | return;
62 | } else {
63 | byte[] bytes = sendText.getBytes();
64 |
65 | // TODO 发送数据 可以输入 A 测试,如果串口调试工具显示为0x41 代表接收正确
66 | mBleController.writeBuffer(bytes, new OnWriteCallback() {
67 | @Override
68 | public void onSuccess() {
69 | Toast.makeText(SendAndReciveActivity.this, "发送成功!", Toast.LENGTH_SHORT).show();
70 | }
71 |
72 | @Override
73 | public void onFailed(int state) {
74 | Toast.makeText(SendAndReciveActivity.this, "发送失败!", Toast.LENGTH_SHORT).show();
75 | }
76 | });
77 | }
78 | break;
79 |
80 | default:
81 |
82 | break;
83 | }
84 | }
85 |
86 | @Override
87 | protected void onDestroy() {
88 | super.onDestroy();
89 |
90 | //移除接收数据的监听
91 | mBleController.unregistReciveListener(REQUESTKEY_SENDANDRECIVEACTIVITY);
92 | // TODO 断开连接
93 | mBleController.closeBleConn();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hansion/hble/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.hansion.hble.sample;
2 |
3 | import android.app.ProgressDialog;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.view.View;
9 | import android.widget.AdapterView;
10 | import android.widget.ListView;
11 | import android.widget.Toast;
12 |
13 | import com.hansion.h_ble.BleController;
14 | import com.hansion.h_ble.callback.ConnectCallback;
15 | import com.hansion.h_ble.callback.ScanCallback;
16 | import com.hansion.hble.R;
17 | import com.hansion.hble.sample.adapter.DeviceListAdapter;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
23 |
24 | private ProgressDialog progressDialog;
25 | private BleController mBleController;
26 | private static String LOGTAG = "AppCompatActivity";
27 | //搜索结果列表
28 | private List bluetoothDevices = new ArrayList();
29 | private ListView mDeviceList;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 |
36 | mDeviceList = (ListView) findViewById(R.id.mDeviceList);
37 |
38 | // TODO 第一步:初始化
39 | mBleController = BleController.getInstance().init(this);
40 |
41 | // TODO 第二步:搜索设备,获取列表后进行展示
42 | scanDevices();
43 |
44 |
45 | }
46 |
47 | private void scanDevices() {
48 |
49 | showProgressDialog("请稍后", "正在搜索设备");
50 |
51 | mBleController.scanBle(0, new ScanCallback() {
52 | @Override
53 | public void onSuccess() {
54 | hideProgressDialog();
55 | if (bluetoothDevices.size() > 0) {
56 | mDeviceList.setAdapter(new DeviceListAdapter(MainActivity.this, bluetoothDevices));
57 | mDeviceList.setOnItemClickListener(MainActivity.this);
58 | } else {
59 | Toast.makeText(MainActivity.this, "未搜索到Ble设备", Toast.LENGTH_SHORT).show();
60 | }
61 | }
62 |
63 | @Override
64 | public void onScanning(BluetoothDevice device, int rssi, byte[] scanRecord) {
65 | if (!bluetoothDevices.contains(device)) {
66 | bluetoothDevices.add(device);
67 | }
68 | }
69 | });
70 | }
71 |
72 |
73 | @Override
74 | protected void onDestroy() {
75 | super.onDestroy();
76 | hideProgressDialog();
77 | }
78 |
79 | public void showProgressDialog(String title, String message) {
80 | if (progressDialog == null) {
81 | progressDialog = ProgressDialog.show(this, title, message, true, false);
82 | } else if (progressDialog.isShowing()) {
83 | progressDialog.setTitle(title);
84 | progressDialog.setMessage(message);
85 | }
86 | progressDialog.show();
87 | }
88 |
89 | public void hideProgressDialog() {
90 | if (progressDialog != null && progressDialog.isShowing()) {
91 | progressDialog.dismiss();
92 | progressDialog = null;
93 | }
94 | }
95 |
96 | @Override
97 | public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
98 | showProgressDialog("请稍后", "正在连接设备");
99 |
100 | // TODO 第三步:点击条目后,获取地址,根据地址连接设备
101 | String address = bluetoothDevices.get(i).getAddress();
102 | mBleController.connect(0, address, new ConnectCallback() {
103 | @Override
104 | public void onConnSuccess() {
105 | hideProgressDialog();
106 | Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
107 | startActivity(new Intent(MainActivity.this,SendAndReciveActivity.class));
108 | }
109 |
110 | @Override
111 | public void onConnFailed() {
112 | hideProgressDialog();
113 | Toast.makeText(MainActivity.this, "连接超时,请重试", Toast.LENGTH_SHORT).show();
114 | }
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/h_ble/src/main/java/com/hansion/h_ble/BleController.java:
--------------------------------------------------------------------------------
1 | package com.hansion.h_ble;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.bluetooth.BluetoothGatt;
6 | import android.bluetooth.BluetoothGattCallback;
7 | import android.bluetooth.BluetoothGattCharacteristic;
8 | import android.bluetooth.BluetoothGattDescriptor;
9 | import android.bluetooth.BluetoothGattService;
10 | import android.bluetooth.BluetoothManager;
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.hansion.h_ble.callback.BleDeviceScanCallback;
18 | import com.hansion.h_ble.callback.ConnectCallback;
19 | import com.hansion.h_ble.callback.OnReceiverCallback;
20 | import com.hansion.h_ble.callback.OnWriteCallback;
21 | import com.hansion.h_ble.callback.ScanCallback;
22 | import com.hansion.h_ble.request.ReceiverRequestQueue;
23 |
24 | import java.util.Arrays;
25 | import java.util.HashMap;
26 | import java.util.Iterator;
27 | import java.util.List;
28 | import java.util.Map;
29 | import java.util.Set;
30 | import java.util.UUID;
31 |
32 | /**
33 | * Description:
34 | * Author: Hansion www.hansion.win
35 | * Time: 2017/2/13 9:43
36 | */
37 | public class BleController {
38 |
39 | private static String LOGTAG = "H_Ble_Lib --> ";
40 |
41 | //BleCOntroller实例
42 | private static BleController sBleManager;
43 | private Context mContext;
44 |
45 | private BluetoothManager mBluetoothManager;
46 | private BluetoothAdapter mAdapter;
47 | private BluetoothGatt mBluetoothGatt;
48 | private BluetoothGattCharacteristic gattCharacteristic;
49 |
50 | private BleGattCallback mGattCallback;
51 | private OnWriteCallback writeCallback;
52 |
53 | private Handler mHandler = new Handler(Looper.getMainLooper());
54 |
55 | //发起连接是否有响应
56 | private boolean isConnectResponse = false;
57 | //获取到所有服务的集合
58 | private HashMap> servicesMap = new HashMap<>();
59 | //默认扫描时间:5s
60 | private static final int SCAN_TIME = 5000;
61 | //默认连接超时时间:6s
62 | private static final int CONNECTION_TIME_OUT = 6000;
63 | //是否是用户手动断开
64 | private boolean isBreakByMyself = false;
65 | //连接结果的回调
66 | private ConnectCallback connectCallback;
67 | //读操作请求队列
68 | private ReceiverRequestQueue mReceiverRequestQueue = new ReceiverRequestQueue();
69 | //此属性一般不用修改
70 | private static final String BLUETOOTH_NOTIFY_D = "00002902-0000-1000-8000-00805f9b34fb";
71 |
72 | //TODO 这里是硬件提供的各种UUID 一定要根据自己的情况进行修改
73 | private static final String BLUETOOTH_S = "0000fff0-0000-1000-8000-00805f9b34fb";
74 | private static final String BLUETOOTH_NOTIFY_C = "0000fff7-0000-1000-8000-00805f9b34fb";
75 | private static final String BLUETOOTH_WRITE_C = "0000fff6-0000-1000-8000-00805f9b34fb";
76 |
77 | //----------------------------- 对外公开的方法 ----------------------------------------------
78 |
79 |
80 |
81 | /**
82 | * 获取BleController实例对象
83 | * @return
84 | */
85 | public synchronized static BleController getInstance() {
86 | if (null == sBleManager) {
87 | sBleManager = new BleController();
88 | }
89 | return sBleManager;
90 | }
91 |
92 |
93 | /**
94 | * 进行初始化
95 | * @param context
96 | * @return
97 | */
98 | public BleController init(Context context) {
99 | if (mContext == null) {
100 | mContext = context.getApplicationContext();
101 | mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
102 | if (null == mBluetoothManager) {
103 | Log.e(LOGTAG, "BluetoothManager init error!");
104 | }
105 |
106 | mAdapter = mBluetoothManager.getAdapter();
107 | if (null == mAdapter) {
108 | Log.e(LOGTAG, "BluetoothManager init error!");
109 | }
110 |
111 | mGattCallback = new BleGattCallback();
112 | }
113 | return this;
114 | }
115 |
116 |
117 | /**
118 | * 扫描设备
119 | * 当传入的time值为0以下时默认扫描时间为5秒
120 | */
121 | public void scanBle(int time, final ScanCallback scanCallback) {
122 | if (!isEnable()) {
123 | mAdapter.enable();
124 | Log.e(LOGTAG, "Bluetooth is not open!");
125 | }
126 | if (null != mBluetoothGatt) {
127 | mBluetoothGatt.close();
128 | }
129 | reset();
130 | final BleDeviceScanCallback bleDeviceScanCallback = new BleDeviceScanCallback(scanCallback);
131 | mHandler.postDelayed(new Runnable() {
132 | @Override
133 | public void run() {
134 | //time后停止扫描
135 | mAdapter.stopLeScan(bleDeviceScanCallback);
136 | scanCallback.onSuccess();
137 | }
138 | }, time <= 0 ? SCAN_TIME : time);
139 |
140 | mAdapter.startLeScan(bleDeviceScanCallback);
141 | }
142 |
143 | /**
144 | * 连接设备
145 | *
146 | * @param connectionTimeOut 连接超时时间,默认是6秒.当赋值为0或更小值时用默认值
147 | * @param devicesAddress 想要连接的设备地址
148 | */
149 | public void connect(final int connectionTimeOut, final String devicesAddress, ConnectCallback connectCallback) {
150 | BluetoothDevice remoteDevice = mAdapter.getRemoteDevice(devicesAddress);
151 | if (null == remoteDevice) {
152 | Log.e(LOGTAG, "No device found at this address:" + devicesAddress);
153 | return;
154 | }
155 |
156 | this.connectCallback = connectCallback;
157 |
158 | if (null != mBluetoothGatt) {
159 | mBluetoothGatt.close();
160 | }
161 | reset();
162 | mBluetoothGatt = remoteDevice.connectGatt(mContext, false, mGattCallback);
163 | Log.e(LOGTAG, "connecting mac-address:" + devicesAddress);
164 | delayConnectResponse(connectionTimeOut);
165 | }
166 |
167 |
168 | /**
169 | * 发送数据
170 | *
171 | * @param buf
172 | * @param writeCallback
173 | */
174 | public void writeBuffer(byte[] buf, OnWriteCallback writeCallback) {
175 | this.writeCallback = writeCallback;
176 | if (!isEnable()) {
177 | writeCallback.onFailed(OnWriteCallback.FAILED_BLUETOOTH_DISABLE);
178 | Log.e(LOGTAG, "FAILED_BLUETOOTH_DISABLE");
179 | return;
180 | }
181 |
182 | if (gattCharacteristic == null) {
183 | gattCharacteristic = getBluetoothGattCharacteristic(BLUETOOTH_S, BLUETOOTH_WRITE_C);
184 | }
185 |
186 | if (null == gattCharacteristic) {
187 | writeCallback.onFailed(OnWriteCallback.FAILED_INVALID_CHARACTER);
188 | Log.e(LOGTAG, "FAILED_INVALID_CHARACTER");
189 | return;
190 | }
191 |
192 | //设置数组进去
193 | gattCharacteristic.setValue(buf);
194 |
195 | //发送
196 | boolean b = mBluetoothGatt.writeCharacteristic(gattCharacteristic);
197 |
198 | Log.e(LOGTAG, "send:" + b + "data:" + bytesToHexString(buf));
199 | }
200 |
201 | /**
202 | * 设置读取数据的监听
203 | *
204 | * @param requestKey
205 | * @param onReceiverCallback
206 | */
207 | public void registReciveListener(String requestKey, OnReceiverCallback onReceiverCallback) {
208 | mReceiverRequestQueue.set(requestKey, onReceiverCallback);
209 | }
210 |
211 | /**
212 | * 移除读取数据的监听
213 | *
214 | * @param requestKey
215 | */
216 | public void unregistReciveListener(String requestKey) {
217 | mReceiverRequestQueue.removeRequest(requestKey);
218 | }
219 |
220 | /**
221 | * 手动断开Ble连接
222 | */
223 | public void closeBleConn() {
224 | disConnection();
225 | isBreakByMyself = true;
226 | gattCharacteristic = null;
227 | mBluetoothManager = null;
228 | }
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | //---------------------------------- 私有方法 ----------------------------------------------
237 |
238 |
239 | /**
240 | * 将byte数组转为16进制字符串 此方法主要目的为方便Log的显示
241 | */
242 | public String bytesToHexString(byte[] src) {
243 | StringBuilder stringBuilder = new StringBuilder("");
244 | if (src == null || src.length <= 0) {
245 | return null;
246 | }
247 | for (byte aSrc : src) {
248 | int v = aSrc & 0xFF;
249 | String hv = Integer.toHexString(v);
250 | if (hv.length() < 2) {
251 | stringBuilder.append(0);
252 | }
253 | stringBuilder.append(hv.toUpperCase()).append(" ");
254 | }
255 | return stringBuilder.toString();
256 | }
257 |
258 | /**
259 | * 当前蓝牙是否打开
260 | */
261 | private boolean isEnable() {
262 | if (null != mAdapter) {
263 | return mAdapter.isEnabled();
264 | }
265 | return false;
266 | }
267 |
268 |
269 | /**
270 | * 复位
271 | */
272 | private void reset() {
273 | isConnectResponse = false;
274 | servicesMap.clear();
275 | }
276 |
277 | /**
278 | * 如果连接connectionTimeOut时间后还没有响应,手动关掉连接.
279 | *
280 | * @param connectionTimeOut
281 | */
282 | private void delayConnectResponse(int connectionTimeOut) {
283 | mHandler.removeCallbacksAndMessages(null);
284 | mHandler.postDelayed(new Runnable() {
285 | @Override
286 | public void run() {
287 | if (!isConnectResponse && !isBreakByMyself) {
288 | Log.e(LOGTAG, "connect timeout");
289 | disConnection();
290 | reConnect();
291 | } else {
292 | isBreakByMyself = false;
293 | }
294 | }
295 | }, connectionTimeOut <= 0 ? CONNECTION_TIME_OUT : connectionTimeOut);
296 | }
297 |
298 |
299 | /**
300 | * 断开连接
301 | */
302 | private void disConnection() {
303 | if (null == mAdapter || null == mBluetoothGatt) {
304 | Log.e(LOGTAG, "disconnection error maybe no init");
305 | return;
306 | }
307 | mBluetoothGatt.disconnect();
308 | reset();
309 | }
310 |
311 |
312 | /**
313 | * 蓝牙GATT连接及操作事件回调
314 | */
315 | private class BleGattCallback extends BluetoothGattCallback {
316 | @Override
317 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
318 |
319 | if (newState == BluetoothProfile.STATE_CONNECTED) { //连接成功
320 | isBreakByMyself = false;
321 | mBluetoothGatt.discoverServices();
322 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //断开连接
323 | if (!isBreakByMyself) {
324 | reConnect();
325 | }
326 | reset();
327 | }
328 | }
329 |
330 | //服务被发现了
331 | @Override
332 | public void onServicesDiscovered(BluetoothGatt gatt, int status) {
333 | if (null != mBluetoothGatt && status == BluetoothGatt.GATT_SUCCESS) {
334 | List services = mBluetoothGatt.getServices();
335 | int serviceSize = services.size();
336 | for (int i = 0; i < serviceSize; i++) {
337 | HashMap charMap = new HashMap<>();
338 | BluetoothGattService bluetoothGattService = services.get(i);
339 | String serviceUuid = bluetoothGattService.getUuid().toString();
340 | List characteristics = bluetoothGattService.getCharacteristics();
341 | int characteristicSize = characteristics.size();
342 | for (int j = 0; j < characteristicSize; j++) {
343 | charMap.put(characteristics.get(j).getUuid().toString(), characteristics.get(j));
344 | if (characteristics.get(j).getUuid().toString().equals(BLUETOOTH_NOTIFY_C)) {
345 | if (enableNotification(true, characteristics.get(j))) {
346 | isConnectResponse = true;
347 | connSuccess();
348 | } else {
349 | reConnect();
350 | }
351 | }
352 | }
353 | servicesMap.put(serviceUuid, charMap);
354 | }
355 | // TODO 打印搜索到的服务
356 | // printServices(mBluetoothGatt);
357 | }
358 | }
359 |
360 | //收到数据
361 | @Override
362 | public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
363 | if (null != mReceiverRequestQueue) {
364 | HashMap map = mReceiverRequestQueue.getMap();
365 | final byte[] rec = characteristic.getValue();
366 | for (String key : mReceiverRequestQueue.getMap().keySet()) {
367 | final OnReceiverCallback onReceiverCallback = map.get(key);
368 | runOnMainThread(new Runnable() {
369 | @Override
370 | public void run() {
371 | onReceiverCallback.onRecive(rec);
372 | }
373 | });
374 | }
375 | }
376 | }
377 |
378 | //描述符被写了
379 | @Override
380 | public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
381 |
382 | }
383 |
384 | //描述符被读了
385 | @Override
386 | public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
387 |
388 | }
389 |
390 | //发送数据结果
391 | @Override
392 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
393 | if (null != writeCallback) {
394 | if (status == BluetoothGatt.GATT_SUCCESS) {
395 | runOnMainThread(new Runnable() {
396 | @Override
397 | public void run() {
398 | writeCallback.onSuccess();
399 | }
400 | });
401 | Log.e(LOGTAG, "Send data success!");
402 | } else {
403 | runOnMainThread(new Runnable() {
404 | @Override
405 | public void run() {
406 | writeCallback.onFailed(OnWriteCallback.FAILED_OPERATION);
407 | }
408 | });
409 | Log.e(LOGTAG, "Send data failed!");
410 | }
411 | }
412 | }
413 | }
414 |
415 |
416 | private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
417 | if (mBluetoothGatt == null || characteristic == null)
418 | return false;
419 | if (!mBluetoothGatt.setCharacteristicNotification(characteristic, enable))
420 | return false;
421 | BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(BLUETOOTH_NOTIFY_D));
422 | if (clientConfig == null)
423 | return false;
424 |
425 | if (enable) {
426 | clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
427 | } else {
428 | clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
429 | }
430 | return mBluetoothGatt.writeDescriptor(clientConfig);
431 | }
432 |
433 |
434 | //打印所有搜索到的服务 此方法用于硬件方未提供UUID时查询所有UUID
435 | private void printServices(BluetoothGatt gatt) {
436 | if (gatt != null) {
437 | Iterator i$ = gatt.getServices().iterator();
438 |
439 | while (i$.hasNext()) {
440 | BluetoothGattService service = (BluetoothGattService) i$.next();
441 | Log.i(LOGTAG, "service: " + service.getUuid());
442 | Iterator i$1 = service.getCharacteristics().iterator();
443 |
444 | while (i$1.hasNext()) {
445 | BluetoothGattCharacteristic characteristic = (BluetoothGattCharacteristic) i$1.next();
446 | Log.d("LOGTAG", " characteristic: " + characteristic.getUuid() + " value: " + Arrays.toString(characteristic.getValue()));
447 | Iterator i$2 = characteristic.getDescriptors().iterator();
448 |
449 | while (i$2.hasNext()) {
450 | BluetoothGattDescriptor descriptor = (BluetoothGattDescriptor) i$2.next();
451 | Log.v("LOGTAG", " descriptor: " + descriptor.getUuid() + " value: " + Arrays.toString(descriptor.getValue()));
452 | }
453 | }
454 | }
455 | }
456 | }
457 |
458 | /**
459 | * 根据服务UUID和特征UUID,获取一个特征{@link BluetoothGattCharacteristic}
460 | *
461 | * @param serviceUUID 服务UUID
462 | * @param characterUUID 特征UUID
463 | */
464 | private BluetoothGattCharacteristic getBluetoothGattCharacteristic(String serviceUUID, String characterUUID) {
465 | if (!isEnable()) {
466 | throw new IllegalArgumentException(" Bluetooth is no enable please call BluetoothAdapter.enable()");
467 | }
468 | if (null == mBluetoothGatt) {
469 | Log.e(LOGTAG, "mBluetoothGatt is null");
470 | return null;
471 | }
472 |
473 | //找服务
474 | Map bluetoothGattCharacteristicMap = servicesMap.get(serviceUUID);
475 | if (null == bluetoothGattCharacteristicMap) {
476 | Log.e(LOGTAG, "Not found the serviceUUID!");
477 | return null;
478 | }
479 |
480 | //找特征
481 | Set> entries = bluetoothGattCharacteristicMap.entrySet();
482 | BluetoothGattCharacteristic gattCharacteristic = null;
483 | for (Map.Entry entry : entries) {
484 | if (characterUUID.equals(entry.getKey())) {
485 | gattCharacteristic = entry.getValue();
486 | break;
487 | }
488 | }
489 | return gattCharacteristic;
490 | }
491 |
492 | private void runOnMainThread(Runnable runnable) {
493 | if (isMainThread()) {
494 | runnable.run();
495 | } else {
496 | if (mHandler != null) {
497 | mHandler.post(runnable);
498 | }
499 | }
500 | }
501 |
502 | private boolean isMainThread() {
503 | return Looper.myLooper() == Looper.getMainLooper();
504 | }
505 |
506 |
507 | // TODO 此方法断开连接或连接失败时会被调用。可在此处理自动重连,内部代码可自行修改,如发送广播
508 | private void reConnect() {
509 | if(connectCallback != null) {
510 | runOnMainThread(new Runnable() {
511 | @Override
512 | public void run() {
513 | connectCallback.onConnFailed();
514 | }
515 | });
516 | }
517 |
518 | Log.e(LOGTAG, "Ble disconnect or connect failed!");
519 | }
520 |
521 | // TODO 此方法Notify成功时会被调用。可在通知界面连接成功,内部代码可自行修改,如发送广播
522 | private void connSuccess() {
523 | if(connectCallback != null) {
524 | runOnMainThread(new Runnable() {
525 | @Override
526 | public void run() {
527 | connectCallback.onConnSuccess();
528 | }
529 | });
530 | }
531 | Log.e(LOGTAG, "Ble connect success!");
532 | }
533 |
534 | }
535 |
--------------------------------------------------------------------------------