Repository: LuoPeiQin/Bluetooth
Branch: master
Commit: 93eb7ea6e843
Files: 123
Total size: 435.5 KB
Directory structure:
gitextract_042z6ibk/
├── .gitignore
├── .idea/
│ ├── codeStyles/
│ │ └── Project.xml
│ ├── compiler.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ └── vcs.xml
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── luo/
│ │ └── bluetooth/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── luo/
│ │ │ └── bluetooth/
│ │ │ ├── MainActivity.java
│ │ │ ├── MyApplication.java
│ │ │ ├── base/
│ │ │ │ └── BaseActivity.java
│ │ │ ├── common/
│ │ │ │ └── Constants.java
│ │ │ ├── customview/
│ │ │ │ └── searchble/
│ │ │ │ ├── BluetoothDeviceAdapter.java
│ │ │ │ ├── BluetoothDeviceBean.java
│ │ │ │ ├── ColorUtils.java
│ │ │ │ ├── DeviceListActivity.java
│ │ │ │ ├── Effectstype.java
│ │ │ │ ├── NiftyDialogBuilder.java
│ │ │ │ ├── SignalView.java
│ │ │ │ └── effects/
│ │ │ │ ├── BaseEffects.java
│ │ │ │ ├── FadeIn.java
│ │ │ │ ├── Fall.java
│ │ │ │ ├── FlipH.java
│ │ │ │ ├── FlipV.java
│ │ │ │ ├── NewsPaper.java
│ │ │ │ ├── RotateBottom.java
│ │ │ │ ├── RotateLeft.java
│ │ │ │ ├── Shake.java
│ │ │ │ ├── SideFall.java
│ │ │ │ ├── SlideBottom.java
│ │ │ │ ├── SlideLeft.java
│ │ │ │ ├── SlideRight.java
│ │ │ │ ├── SlideTop.java
│ │ │ │ └── Slit.java
│ │ │ ├── encryption/
│ │ │ │ ├── Aes.java
│ │ │ │ └── CRC.java
│ │ │ ├── protocol/
│ │ │ │ ├── CRC16Utils.java
│ │ │ │ ├── CrcUtils.java
│ │ │ │ ├── CustomEventListener.java
│ │ │ │ ├── CustomPacket.java
│ │ │ │ ├── CustomProtocol.java
│ │ │ │ ├── OnTimeoutResult.java
│ │ │ │ ├── WeiCeCode.java
│ │ │ │ └── WeiCeDeviceOperate.java
│ │ │ └── utils/
│ │ │ ├── ByteUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── DialogUtils.java
│ │ │ ├── GpsUtils.java
│ │ │ ├── GsonUtils.java
│ │ │ ├── LogUtils.java
│ │ │ ├── TextUtils.java
│ │ │ └── ToastUtil.java
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── fade_in.xml
│ │ │ └── fade_out.xml
│ │ ├── drawable/
│ │ │ ├── btn_press.xml
│ │ │ ├── btn_selector.xml
│ │ │ ├── btn_unpress.xml
│ │ │ ├── dialog_bg.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ └── sel_white_gray.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_search_device_list.xml
│ │ │ ├── dialog_layout.xml
│ │ │ └── item_bluetooth_device_list.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── luo/
│ └── bluetooth/
│ └── ExampleUnitTest.java
├── bluetooth/
│ ├── .gitignore
│ ├── build.gradle
│ ├── libs/
│ │ ├── com.broadcom.bt.jar
│ │ └── samsung_ble_sdk_200.jar
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── stag/
│ │ └── bluetooth/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── stag/
│ │ │ └── bluetooth/
│ │ │ ├── BluetoothController.java
│ │ │ ├── BluetoothDispatch.java
│ │ │ ├── BluetoothTask.java
│ │ │ ├── BluetoothTransfer.java
│ │ │ ├── OnBluetoothConnectStateChangeListener.java
│ │ │ ├── OnBluetoothScanListener.java
│ │ │ ├── OnBluetoothStateChangeListener.java
│ │ │ ├── OnBluetoothTransmitListener.java
│ │ │ ├── extend/
│ │ │ │ ├── AndroidBle.java
│ │ │ │ ├── BleGattCharacteristic.java
│ │ │ │ ├── BleGattService.java
│ │ │ │ ├── BleRequest.java
│ │ │ │ ├── BleService.java
│ │ │ │ ├── BroadcomBle.java
│ │ │ │ ├── IBle.java
│ │ │ │ ├── IBleRequestHandler.java
│ │ │ │ └── SamsungBle.java
│ │ │ ├── helper/
│ │ │ │ ├── BleHelper.java
│ │ │ │ ├── BluetoothHelper.java
│ │ │ │ ├── MyBluetoothService.java
│ │ │ │ ├── TraditionHelper.java
│ │ │ │ └── TraditionServerHelper.java
│ │ │ ├── packet/
│ │ │ │ └── Packet.java
│ │ │ ├── protocol/
│ │ │ │ ├── OnEventListener.java
│ │ │ │ ├── ParseResult.java
│ │ │ │ ├── Protocol.java
│ │ │ │ └── ResultType.java
│ │ │ └── util/
│ │ │ ├── ByteUtils.java
│ │ │ ├── LogUtils.java
│ │ │ └── Logs.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── stag/
│ └── bluetooth/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
================================================
FILE: .idea/codeStyles/Project.xml
================================================
.*:id
http://schemas.android.com/apk/res/android
.*:name
http://schemas.android.com/apk/res/android
.*
http://schemas.android.com/apk/res/android
ANDROID_ATTRIBUTE_ORDER
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/jarRepositories.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: README.md
================================================
# Bluetooth
## 该库的特点
1. 内部集成了多种蓝牙芯片的操作,能兼容几乎市面上所有的蓝牙设备;
2. 支持低功耗蓝牙和传统蓝牙;
3. 支持设置低功耗蓝牙的高速传输模式;
4. 实现了蓝牙的重发机制;
5. 实现了蓝牙的同异步发送数据;
6. 实现了协议基类封装,开发者可以快速扩展自己的协议;
**注意:部分Android6.0以上的手机需要定位权限才能正常使用蓝牙功能**
## 库地址
[  ](https://bintray.com/luopeiqin/maven/bluetooth/1.0.2/link)
```
implementation 'com.stag:bluetooth:1.0.2'
```
## 蓝牙搜索自定义View
项目中增加了蓝牙搜素的自定义View,方便你快速的实现项目
### 自定义View包含的内容
1. 根据蓝牙的信号强度排序和显示;
2. 有搜素或连接的历史记录功能(做小部分配置即可实现);
## 蓝牙基本操作相关接口说明
> **调用的类为com.stag.bluetooth.BluetoothControl.java**
### 获取单例对象
```java
BluetoothController mController = BluetoothController.getController(this);
```
### 注册蓝牙状态监听
```java
mController.registerBluetoothStateChangeListener(new OnBluetoothStateChangeListener() {
@Override
public void onBluetoothOpen() {
LogUtils.i(TAG + "lpq", "onBluetoothOpen: 蓝牙打开");
}
@Override
public void onBluetoothClose() {
LogUtils.i(TAG + "lpq", "onBluetoothClose: 蓝牙关闭");
}
});
```
### 注册蓝牙连接状态变化监听
```java
mController.registerConnectStateChangeListener(new OnBluetoothConnectStateChangeListener() {
@Override
public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {
if (isSuccess) {
LogUtils.i(TAG + "lpq", "onBluetoothConnect: 蓝牙已连接");
} else {
LogUtils.i(TAG + "lpq", "onBluetoothConnect: 蓝牙连接失败");
}
}
@Override
public void onBluetoothDisconnect(BluetoothDevice device) {
LogUtils.i(TAG + "lpq", "onBluetoothDisconnect: 蓝牙已断开");
}
});
```
### 切换蓝牙类型
```java
mController.setBluetoothType(BluetoothType.BLE); // 低功耗蓝牙
mController.setBluetoothType(BluetoothType.TRADITION); // 传统蓝牙
```
### 搜索蓝牙
```java
mController.startScan(new OnBluetoothScanListener() {
@Override
public void onBluetoothScanFindDevice(BluetoothDevice device, int rssi) {
LogUtils.i(TAG + "lpq", "onBluetoothScanFindDevice: " + device.getAddress());
}
@Override
public void onBluetoothScanFinish() {
LogUtils.i(TAG + "lpq", "onBluetoothScanFinish: ");
}
});
```
### 停止搜索蓝牙
```java
mController.stopScan();
```
### 连接蓝牙与断开蓝牙连接
```java
mController.connect("0C:B2:B7:3E:23:60"); //连接蓝牙,参数为蓝牙MAC地址
mController.disconnect(); //断开连接
```
### 低功耗蓝牙时开启高速模式
```java
mController.setBleHighSpeedMode(true);
```
不一定100%成功,由蓝牙设备与App设备蓝牙的最低MTU决定。
### 取消注册监听回调
```java
@Override
protected void onDestroy() {
super.onDestroy();
if (mController != null) {
mController.unregisterBluetoothStateChangeListener();
mController.unregisterConnectStateChangeListener();
}
}
```
## 蓝牙收发数据重点说明
### 方式一:继承蓝牙内置协议
```java
public abstract class Protocol {
public final static int BLE_MAX_SEND_INTERVAL = 500;
protected Context mContext;
private T mEventListener;
private Object mData;
private int mMaxBleSendInterval = BLE_MAX_SEND_INTERVAL;
/**
* 协议所特有的主动事件监听
*/
protected Protocol(Context context) {
this(context, null);
}
/**
* 协议所特有的主动事件监听
*/
protected Protocol(Context context, T listener) {
mContext = context.getApplicationContext();
mEventListener = listener;
}
/**
* 发送包处理成最终要发送的字节数据
*/
public abstract byte[] packetToBytes(E packet);
/**
* 解析收到的字节处理成结果
*/
public abstract ParseResult parse(byte[] data);
/**
* 获取协议类型
* */
public abstract int getType();
/**
* 是否设置了主动事件监听
* */
protected boolean haveSetEventListener(){
return mEventListener!=null;
}
······
}
```
#### 内置协议简要说明
上述抽象类中屏蔽了部分内容,我们主要看几个重点:
1. **packetToBytes** 和 **parse**是用户层面发送数据的最终端和接收数据的最初端,您可以根据自己蓝牙协议的需要来重写方法,创建自己的协议类;
2. **getType**是用来支持App需要同时支持多个蓝牙协议的情况的;
3. **mEventListener**用于接收蓝牙设备主动上报的一些状态;
#### 设置蓝牙传输协议
> 该方法是与蓝牙设备操作相关的方法,而且必须在连接蓝牙设备之前设置
```java
mController.setProtocol(new Protocol(this, this));
```
#### 创建异步发送数据任务
```java
BluetoothTask task = new BluetoothTask(new Packet(cmd, data), new BluetoothTask.OnDataResultListener() {
@Override
public void onResult(boolean isTimeout, byte[] data) {
//数据接收回调,异步时使用
}
});
task.setTimeout(2000); //设置超时时间
task.setTryCount(1); //设置重发次数
task.send(); //异步发送数据
```
#### 创建同步发送数据任务
```java
BluetoothTask task = new BluetoothTask(new Packet(cmd, data));
task.setTimeout(2000); //设置超时时间
task.setTryCount(1); //设置重发次数
byte[] result = task.sendBySync2(); //同步发送数据
```
### 方式二:原始数据收发
如果方式一没有办法满足你的要求,那么我也提供方式二来供你选择
#### 设置原始数据收发监听
```java
mController.registerTransmitListener(new OnBluetoothTransmitListener() {
@Override
public void onBluetoothSendData(byte[] data) {
// 发送数据
}
@Override
public void onBluetoothRecvData(byte[] data) {
// 接收数据
}
});
```
#### 发送数据
```java
mController.sendData(new byte[]{});
```
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'bugly'
bugly {
appId = '4efc7ff5d2' // 注册时分配的App ID
appKey = '' // 注册时分配的App Key
}
android {
signingConfigs {
release {
}
}
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.luo.bluetooth"
minSdkVersion 21
targetSdkVersion 29
versionCode 3
versionName "1.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true // 启用混淆
zipAlignEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
compileOptions{
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "Bluetooth_${defaultConfig.versionName}.apk"
}
}
}
repositories{
flatDir{
dirs 'libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(path: ':bluetooth')
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// 注解类
implementation 'org.projectlombok:lombok:1.18.12'
android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
// UI
implementation 'com.github.f0ris.sweetalert:library:1.5.1'
implementation 'androidx.cardview:cardview:1.0.0'
// 网络
implementation 'com.squareup.retrofit2:converter-gson:2.8.0'
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1'
// 工具类
implementation 'com.blankj:utilcodex:1.28.0'
// Bugly
implementation 'com.tencent.bugly:crashreport_upgrade:latest.release'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify
# 保留Annotation不混淆 这在JSON实体映射时非常重要,比如fastJson
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
# 忽略警告
-ignorewarnings
# 设置是否允许改变作用域
-allowaccessmodification
# 把混淆类中的方法名也混淆了
-useuniqueclassmembernames
# apk 包内所有 class 的内部结构
-dump class_files.txt
# 未混淆的类和成员
#-printseeds seeds_txt
# 列出从apk中删除的代码
#-printusage unused.txt
#保持源码的行号、源文件信息不被混淆 方便在崩溃日志中查看
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
# 四大组件不能混淆,四大组件必须在 manifest 中注册声明
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgent
-keep public class * extends android.preference.Preference
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment
-keep public class * extends android.view.view
-keep public class com.android.vending.licensing.ILicensingService
# 不能混淆枚举中的value和valueOf方法
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 不混淆bean类
-keep class com.luo.bluetooth.blebean.** {*;}
-keep class com.luo.bluetooth.netbean.** {*;}
# gson
-keep class com.google.gson.** {*;}
-keep class sun.misc.Unsafe {*;}
-keep class com.google.gson.stream.** {*;}
-keep class com.google.gson.examples.android.model.** {*;}
-keep class com.google.** {
;
;
}
-dontwarn com.google.gson.**
# Bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
-keep class android.support.**{*;}
#ddi
-keep class com.xinguodu.ddiinterface.**{*;}
#SmartPOSJni
-keep class com.nexgo.oaf.smartpos.jni.SmartPOSJni{*;}
#API
-keep class com.nexgo.oaf.smartpos.CardAPI {*;}
-keep class com.nexgo.oaf.smartpos.DeviceAPI {*;}
-keep class com.nexgo.oaf.smartpos.KeyAPI {*;}
-keep class com.nexgo.oaf.smartpos.OtherAPI {*;}
-keep class com.nexgo.oaf.smartpos.PeripheralAPI {*;}
#DeviceEngineImpl
-keep class com.nexgo.oaf.smartpos.apiv3.DeviceEngineImpl {*;}
-keep class com.nexgo.oaf.apiv3.DeviceInfo {*;}
-keep class cn.pedant.** { *; }
================================================
FILE: app/src/androidTest/java/com/luo/bluetooth/ExampleInstrumentedTest.java
================================================
package com.luo.bluetooth;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.luo.bluetooth", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/luo/bluetooth/MainActivity.java
================================================
package com.luo.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.luo.bluetooth.customview.searchble.BluetoothDeviceBean;
import com.luo.bluetooth.customview.searchble.DeviceListActivity;
import com.luo.bluetooth.protocol.CustomPacket;
import com.luo.bluetooth.protocol.CustomProtocol;
import com.luo.bluetooth.protocol.OnTimeoutResult;
import com.luo.bluetooth.protocol.WeiCeCode;
import com.luo.bluetooth.protocol.WeiCeDeviceOperate;
import com.luo.bluetooth.utils.ByteUtils;
import com.luo.bluetooth.utils.DialogUtils;
import com.luo.bluetooth.utils.GpsUtils;
import com.luo.bluetooth.utils.LogUtils;
import com.luo.bluetooth.utils.ToastUtil;
import com.stag.bluetooth.BluetoothController;
import com.stag.bluetooth.OnBluetoothConnectStateChangeListener;
import com.stag.bluetooth.OnBluetoothStateChangeListener;
import java.nio.charset.StandardCharsets;
import cn.pedant.SweetAlert.SweetAlertDialog;
public class MainActivity extends AppCompatActivity implements OnBluetoothStateChangeListener, OnBluetoothConnectStateChangeListener {
private static final String TAG = "MainActivity";
private static final int QUEST_BLUETOOTH_DEVICE = 1;
private TextView appVersion;
private TextView bleStatus;
private TextView bleConnectStatus;
// 蓝牙相关
public static BluetoothDeviceBean mDeviceModel;
public BluetoothController mBluetoothController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initBluetooth();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
public void onDestroy() {
if (mBluetoothController != null) {
mBluetoothController.setProtocol(null);
mBluetoothController.unregisterBluetoothStateChangeListener();
mBluetoothController.unregisterConnectStateChangeListener();
mBluetoothController.disconnect();
mBluetoothController.stopScan();
}
DialogUtils.dismissDialog();
super.onDestroy();
}
private void initView() {
appVersion = findViewById(R.id.app_version);
bleStatus = findViewById(R.id.ble_status);
bleConnectStatus = findViewById(R.id.ble_connect_status);
appVersion.setText("" + BuildConfig.VERSION_NAME + " - " + BuildConfig.VERSION_CODE);
}
/**
* 初始化蓝牙框架
*/
private void initBluetooth() {
mBluetoothController = BluetoothController.getController(this);
mBluetoothController.registerBluetoothStateChangeListener(this);
mBluetoothController.registerConnectStateChangeListener(this);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
LogUtils.d(TAG + "lpq", "initBluetooth: 该设备不支持蓝牙");
ToastUtil.showShort(this, "该设备不支持蓝牙!无法正常使用");
return;
}
if (!bluetoothAdapter.isEnabled()) {
bleStatus.setText("已关闭");
bluetoothAdapter.enable();
} else {
bleStatus.setText("已打开");
}
/**
* 可以选择作为客户端搜索传统蓝牙,低功耗蓝牙
* 或者作为传统蓝牙服务端
*/
mBluetoothController.setBluetoothType(BluetoothController.TYPE_BLE);
}
/**
* 搜索和连接蓝牙
*
* @param view
*/
public void btnSearchBle(View view) {
if (!GpsUtils.isOPen(this)) {
DialogUtils.showNormalDialog(this, "提示", "为保证初始化功能的正常使用,请开启定位功能", new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
DialogUtils.dismissDialog();
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
});
return;
}
if (mBluetoothController.isConnected()) {
mBluetoothController.disconnect();
} else {
Intent deviceIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(deviceIntent, QUEST_BLUETOOTH_DEVICE);
}
}
/**
* 使用mac地址连接蓝牙
*
* @param view
*/
public void btnMacConnect(View view) {
mBluetoothController.connect("40:C8:1F:6A:CD:2B", new CustomProtocol(this, null));
}
/**
* 断开蓝牙
*/
public void btnDisconnectBle(View view) {
LogUtils.d(TAG + "lpq", "btnDisconnectBle: ");
if (mBluetoothController.isConnected()) {
mBluetoothController.disconnect();
} else {
mBluetoothController.stopScan();
}
}
/**
* 蓝牙搜索结果
*
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(int requestCode, int resultCode, final Intent data) {
if (resultCode != RESULT_OK) {
return;
}
mDeviceModel = (BluetoothDeviceBean) data.getSerializableExtra(DeviceListActivity.KEY_SCAN_DEVICE);
LogUtils.d(TAG + "lpq", "onActivityResult: " + mDeviceModel.toString());
String deviceName = mDeviceModel.getDeviceName();
if (deviceName != null) {
if (mDeviceModel.isBle()) {
mBluetoothController.setBluetoothType(BluetoothController.TYPE_BLE);
} else {
mBluetoothController.setBluetoothType(BluetoothController.TYPE_TRADITION);
}
mBluetoothController.setProtocol(new CustomProtocol(this, null));
mBluetoothController.connect(mDeviceModel.getDeviceAddress());
} else {
ToastUtil.showShort(this, "蓝牙名称为空,请重试");
}
}
/********************************* 蓝牙监听回调 *************************************/
@Override
public void onBluetoothOpen() {
LogUtils.d(TAG + "lpq", "onBluetoothOpen: 蓝牙已打开");
bleStatus.setText("已打开");
}
@Override
public void onBluetoothClose() {
LogUtils.d(TAG + "lpq", "onBluetoothClose: 蓝牙已关闭");
bleStatus.setText("已关闭");
}
/**
* 蓝牙连接事件回调
*
* @param device 蓝牙设备
* @param isSuccess 是否成功
*/
@Override
public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {
LogUtils.d(TAG + "lpq", "onBluetoothConnect: 蓝牙连接结果:" + isSuccess);
if (isSuccess) {
bleConnectStatus.setText("已连接");
mBluetoothController.setProtocol(new CustomProtocol(this, null));
} else {
bleConnectStatus.setText("未连接");
}
}
/**
* 蓝牙断开连接事件回调
*
* @param device 蓝牙设备
*/
@Override
public void onBluetoothDisconnect(BluetoothDevice device) {
bleConnectStatus.setText("未连接");
}
/**
* 协议解析测试
*
* @param view
*/
public void btnProtocolParse(View view) {
LogUtils.d(TAG + "lpq", "btnProtocolParse: 蓝牙协议解析测试");
CustomProtocol customProtocol = new CustomProtocol(this, null);
String a1String = "02413130313139323032303";
byte[] bytes = ByteUtils.hexStringToBytes(a1String);
LogUtils.d(TAG + "lpq", "btnProtocolParse: bytes = " + ByteUtils.toString(bytes, ""));
customProtocol.parse(bytes);
}
/**
* 协议打包测试
*
* @param view
*/
public void btnProtocolPacket(View view) {
LogUtils.d(TAG + "lpq", "btnProtocolParse: 蓝牙协议解析测试");
CustomProtocol customProtocol = new CustomProtocol(this, null);
byte[] sendBytes = "00".getBytes(StandardCharsets.US_ASCII);
LogUtils.d(TAG + "lpq", "confirm: sendBytes = " + ByteUtils.toString(sendBytes));
new CustomProtocol(this, null).packetToBytes(new CustomPacket(0x01 /* 命令码 */, sendBytes, WeiCeCode.EXPAND_CODE_READ));
}
/**
* 发送简单数据
* 这个不会走封装好的协议
*
* @param view
*/
public void btnSendConfirmData(View view) {
WeiCeDeviceOperate.getSn(new OnTimeoutResult() {
@Override
public void onResult(boolean isTimeout, String result) {
Log.i("lpq", "onResult: isTimeout = " + isTimeout);
Log.i("lpq", "onResult: sn = " + result);
}
}, true);
// byte[] bytes = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
// mBluetoothController.sendData(bytes);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/MyApplication.java
================================================
package com.luo.bluetooth;
import android.app.Application;
import com.tencent.bugly.Bugly;
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
@Override
public void onCreate() {
super.onCreate();
Bugly.init(getApplicationContext(), "4efc7ff5d2", false);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/base/BaseActivity.java
================================================
package com.luo.bluetooth.base;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.lang.ref.WeakReference;
public class BaseActivity extends AppCompatActivity {
//全局变量
protected static boolean isConnecting = false;
protected static boolean isLockConnecting = false;
public static WeakReference currentResumeContext;
protected Context mContext;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
@Override
protected void onResume() {
currentResumeContext = new WeakReference(this);
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public void startToActivity(Class activityClass) {
Intent intent = new Intent(this, activityClass);
startActivity(intent);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/common/Constants.java
================================================
package com.luo.bluetooth.common;
public class Constants {
/**
* 蓝牙相关
*/
//蓝牙连接状态
public static final int BLUETOOTH_STATUS_INIT = 0;//蓝牙钥匙状态,未连接过
public static final int BLUETOOTH_STATUS_CONNECTED = 1;//蓝牙钥匙状态,连接过
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/BluetoothDeviceAdapter.java
================================================
package com.luo.bluetooth.customview.searchble;
import android.content.Context;
import android.graphics.Color;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.luo.bluetooth.R;
import com.luo.bluetooth.common.Constants;
import java.util.List;
/**
* 蓝牙搜索界面适配器
* Created by Administrator on 2016/7/23 0023.
*/
public class BluetoothDeviceAdapter extends BaseAdapter{
private Context mContext;
private List mModels;
public BluetoothDeviceAdapter(Context mContext, List mModels) {
this.mContext = mContext;
this.mModels = mModels;
}
@Override
public int getCount() {
return mModels.size();
}
@Override
public Object getItem(int i) {
return mModels.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder holder=null;
if(view==null){
view= LayoutInflater.from(mContext).inflate(R.layout.item_bluetooth_device_list,null);
holder=new ViewHolder();
holder.tvName= (TextView) view.findViewById(R.id.tv_bluetooth_name);
holder.tvAddress= (TextView) view.findViewById(R.id.tv_bluetooth_address);
holder.svBluetooth= (SignalView) view.findViewById(R.id.svBluetooth);
view.setTag(holder);
}else{
holder= (ViewHolder) view.getTag();
}
initView(i,holder);
return view;
}
public void initView(int i,ViewHolder holder){
BluetoothDeviceBean device=mModels.get(i);
StringBuffer buf=new StringBuffer();
buf.append(device.getDeviceName());
if(!TextUtils.isEmpty(device.getNickName())){
buf.append("【"+device.getNickName()+"】");
}
holder.tvName.setText(buf.toString());
if(device.getStatus()== Constants.BLUETOOTH_STATUS_CONNECTED){
holder.tvAddress.setTextColor(Color.BLUE);
}else{
holder.tvAddress.setTextColor(Color.RED);
}
holder.tvAddress.setText(device.getDeviceAddress());
int intensity = (device.getRssi()+100)/12;
holder.svBluetooth.setIntensity(intensity);
// Log.d("LPQ3", "name:"+holder.tvAddress.getText()+" rssi:"+device.getRssi()+" intensity:"+intensity);
}
private class ViewHolder{
public TextView tvName;
public TextView tvAddress;
public SignalView svBluetooth;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/BluetoothDeviceBean.java
================================================
package com.luo.bluetooth.customview.searchble;
import java.io.Serializable;
import lombok.Data;
/**
* Created by Administrator on 2016/7/23 0023.
*/
@Data
public class BluetoothDeviceBean implements Serializable {
private String deviceName;
private String deviceAddress;
private int status;
private boolean isBle;
private String nickName;
private int rssi;
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/ColorUtils.java
================================================
package com.luo.bluetooth.customview.searchble;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
// http://stackoverflow.com/a/11171509/317862
public class ColorUtils {
public static ColorFilter getColorFilter(int color) {
ColorMatrixColorFilter colorFilter;
int red = (color & 0xFF0000) / 0xFFFF;
int green = (color & 0xFF00) / 0xFF;
int blue = color & 0xFF;
float[] matrix = { 0, 0, 0, 0, red
, 0, 0, 0, 0, green
, 0, 0, 0, 0, blue
, 0, 0, 0, 1, 0 };
colorFilter = new ColorMatrixColorFilter(matrix);
return colorFilter;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/DeviceListActivity.java
================================================
package com.luo.bluetooth.customview.searchble;
import android.Manifest;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.stag.bluetooth.BluetoothController;
import com.stag.bluetooth.OnBluetoothScanListener;
import com.luo.bluetooth.R;
import com.luo.bluetooth.base.BaseActivity;
import com.luo.bluetooth.common.Constants;
import com.luo.bluetooth.utils.DialogUtils;
import com.luo.bluetooth.utils.LogUtils;
import java.util.LinkedList;
import java.util.List;
import cn.pedant.SweetAlert.SweetAlertDialog;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
/**
* 搜索蓝牙设备界面
* Created by Administrator on 2016/7/22 0022.
*/
public class DeviceListActivity extends BaseActivity implements View.OnClickListener, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, OnBluetoothScanListener {
private static final String TAG = "DeviceListActivity";
public static final String KEY_SCAN_DEVICE = "KEY_SCAN_DEVICE";
private Button btnSearch, btnHistory, btnCancel;
private ListView lvDevices;
private TextView tvTitle, tvNoKey;
private ProgressBar pbScanning;
private BluetoothDeviceAdapter adapter;
private List mDeviceModes = new LinkedList();
private BluetoothController mBluetoothController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LogUtils.d(TAG + ": lpq", "onCreate: ");
setContentView(R.layout.activity_search_device_list);
findViews();
initData();
bindEvent();
this.setFinishOnTouchOutside(false);
}
@Override
public void finish() {
LogUtils.d(TAG + ": lpq", "finish: ");
super.finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
@Override
protected void onDestroy() {
LogUtils.d(TAG + ": lpq", "onDestroy: ");
mBluetoothController.stopScan();
super.onDestroy();
}
protected void findViews() {
lvDevices = (ListView) findViewById(R.id.lv_devices);
tvTitle = (TextView) findViewById(R.id.tvTitle);
tvNoKey = (TextView) findViewById(R.id.tv_no_key);
pbScanning = (ProgressBar) findViewById(R.id.pbScanning);
btnSearch = (Button) findViewById(R.id.btn_search);
btnHistory = (Button) findViewById(R.id.btn_history);
btnCancel = (Button) findViewById(R.id.btn_cancel);
}
public void initData() {
mBluetoothController = BluetoothController.getController(this);
tvTitle.setText("设备列表");
lvDevices.setEmptyView(tvNoKey);
adapter = new BluetoothDeviceAdapter(this, mDeviceModes);
lvDevices.setAdapter(adapter);
startSearchKey();
getPermissions();
}
/**
* 获取权限
*/
private void getPermissions() {
if (ContextCompat.checkSelfPermission(DeviceListActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//请求权限
ActivityCompat.requestPermissions(DeviceListActivity.this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1);
//判断是否需要 向用户解释,为什么要申请该权限
ActivityCompat.shouldShowRequestPermissionRationale(DeviceListActivity.this,
Manifest.permission.READ_CONTACTS);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults != null && grantResults.length > 0) {
if (grantResults[0] == PERMISSION_GRANTED) {
startSearchKey();
} else {
DialogUtils.showErrorDialog(mContext, "没有权限无法使用蓝牙", new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
DialogUtils.dismissDialog();
finish();
}
});
}
}
}
private void startSearchKey() {
LogUtils.d(TAG + ": lpq", "startSearchKey: ");
tvTitle.setText(R.string.title_device_list_search_key);
showProgressBar();
mDeviceModes.clear();
adapter.notifyDataSetChanged();
mBluetoothController.startScan(this);
}
private void stopSearchKey() {
LogUtils.d(TAG + ": lpq", "stopSearchKey: ");
tvTitle.setText(R.string.title_device_list_stop_search);
hideProgressBar();
mBluetoothController.stopScan();
}
private void showHistory() {
stopSearchKey();
tvTitle.setText(R.string.title_device_list_history);
hideProgressBar();
mDeviceModes.clear();
adapter.notifyDataSetChanged();
}
protected void bindEvent() {
btnSearch.setOnClickListener(this);
btnCancel.setOnClickListener(this);
btnHistory.setOnClickListener(this);
lvDevices.setOnItemClickListener(this);
lvDevices.setOnItemLongClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_search:
startSearchKey();
break;
case R.id.btn_history:
showHistory();
break;
case R.id.btn_cancel:
stopSearchKey();
finish();
break;
default:
break;
}
}
@Override
public void onItemClick(AdapterView> adapterView, View view, final int i, long l) {
BluetoothDeviceBean model = mDeviceModes.get(i);
model.setStatus(Constants.BLUETOOTH_STATUS_CONNECTED);
//连接成功之后再保存会好一点
// model.saveOrUpdate("deviceAddress = ?", model.getDeviceAddress());
Intent intent = new Intent();
intent.putExtra(KEY_SCAN_DEVICE, model);
setResult(RESULT_OK, intent);
stopSearchKey();
finish();
}
@Override
public boolean onItemLongClick(AdapterView> adapterView, View view, int i, long l) {
showSetNickNameDialog(this, mDeviceModes.get(i));
return true;
}
public void showSetNickNameDialog(Context context, final BluetoothDeviceBean model) {
final EditText editText = new EditText(this);
editText.setText(model.getNickName());
final NiftyDialogBuilder dialogBuilder = NiftyDialogBuilder.getInstance(context);
dialogBuilder.withTitle(getResources().getString(R.string.dialog_set_note_name))//.withTitle(null) no title
.withMessage(null)
.withEffect(Effectstype.values()[9])
.withTitleColor("#FFFFFF") //def
.withDividerColor("#11000000") //def
.withMessageColor("#FFFFFFFF") //def | withMessageColor(int resid)
.withDialogColor(getResources().getColor(R.color.colorPrimary)) //def | withDialogColor(int resid)
// .withIcon(context.getResources().getDrawable(R.mipmap.logo))
.withDuration(700) //def
// .withEffect(effect) //def Effectstype.Slidetop
.withButton1Text(context.getString(R.string.btn_ok)) //def gone
.isCancelableOnTouchOutside(false) //def | isCancelable(true)
// .setCustomView(R.layout.custom_view,v.getContext()) //.setCustomView(View or ResId,context)
.setCustomView(editText, this)
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
model.setNickName(editText.getText().toString());
// model.saveOrUpdate("deviceAddress = ?", model.getDeviceAddress());
adapter.notifyDataSetChanged();
dialogBuilder.dismiss();
}
}).withButton2Text(context.getString(R.string.btn_cancel))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialogBuilder.dismiss();
}
})
.show();
}
@Override
public void onBluetoothScanFindDevice(BluetoothDevice device, int rssi, boolean isBle) {
for (BluetoothDeviceBean m : mDeviceModes) {
if (m.getDeviceAddress().equals(device.getAddress())) {
return;
}
}
Log.i("lpq", "onBluetoothScanFindDevice: device.name = " + device.getName() + "device.mac = " + device.getAddress());
// List deviceBeans = LitePal.where("deviceAddress = ?", device.getAddress()).find(BluetoothDeviceBean.class);
// BluetoothDeviceBean model = deviceBeans.size() == 0 ? null : deviceBeans.get(0);
BluetoothDeviceBean model = null;
if (model == null) {
model = new BluetoothDeviceBean();
model.setDeviceAddress(device.getAddress());
model.setDeviceName(device.getName());
model.setStatus(Constants.BLUETOOTH_STATUS_INIT);
model.setBle(isBle);
} else {
model.setDeviceName(device.getName());
//感觉这句话没有什么用
// model.saveOrUpdate("deviceAddress = ?", model.getDeviceAddress());
// BluetoothDeviceUtil.saveBluetoothDevice(mContext, model);
}
model.setRssi(rssi);
int index = -1;
for (int i=0;i effectsClazz;
private Effectstype(Class extends BaseEffects> mclass) {
effectsClazz = mclass;
}
public BaseEffects getAnimator() {
BaseEffects bEffects=null;
try {
bEffects = effectsClazz.newInstance();
} catch (ClassCastException e) {
throw new Error("Can not init animatorClazz instance");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
throw new Error("Can not init animatorClazz instance");
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
throw new Error("Can not init animatorClazz instance");
}
return bEffects;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/NiftyDialogBuilder.java
================================================
package com.luo.bluetooth.customview.searchble;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.luo.bluetooth.R;
import com.luo.bluetooth.customview.searchble.effects.BaseEffects;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class NiftyDialogBuilder extends Dialog implements DialogInterface {
private final String defTextColor="#FFFFFFFF";
private final String defDividerColor="#11000000";
private final String defMsgColor="#FFFFFFFF";
private final String defDialogColor="#FFE74C3C";
private static Context tmpContext;
private Effectstype type=null;
private LinearLayout mLinearLayoutView;
private RelativeLayout mRelativeLayoutView;
private LinearLayout mLinearLayoutMsgView;
private LinearLayout mLinearLayoutTopView;
private FrameLayout mFrameLayoutCustomView;
private View mDialogView;
private View mDivider;
private TextView mTitle;
private TextView mMessage;
private ImageView mIcon;
private Button mButton1;
private Button mButton2;
private int mDuration = -1;
private static int mOrientation=1;
private boolean isCancelable=true;
private static NiftyDialogBuilder instance;
public NiftyDialogBuilder(Context context) {
super(context);
init(context);
}
public NiftyDialogBuilder(Context context, int theme) {
super(context, theme);
init(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager.LayoutParams params = getWindow().getAttributes();
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
getWindow().setAttributes((WindowManager.LayoutParams) params);
}
public static NiftyDialogBuilder getInstance(Context context) {
if (instance == null || !tmpContext.equals(context)) {
synchronized (NiftyDialogBuilder.class) {
if (instance == null || !tmpContext.equals(context)) {
instance = new NiftyDialogBuilder(context, R.style.dialog_untran);
}
}
}
tmpContext = context;
return instance;
}
private void init(Context context) {
mDialogView = View.inflate(context, R.layout.dialog_layout, null);
mLinearLayoutView=(LinearLayout)mDialogView.findViewById(R.id.parentPanel);
mRelativeLayoutView=(RelativeLayout)mDialogView.findViewById(R.id.main);
mLinearLayoutTopView=(LinearLayout)mDialogView.findViewById(R.id.topPanel);
mLinearLayoutMsgView=(LinearLayout)mDialogView.findViewById(R.id.contentPanel);
mFrameLayoutCustomView=(FrameLayout)mDialogView.findViewById(R.id.customPanel);
mTitle = (TextView) mDialogView.findViewById(R.id.alertTitle);
mMessage = (TextView) mDialogView.findViewById(R.id.message);
mIcon = (ImageView) mDialogView.findViewById(R.id.icon);
mDivider = mDialogView.findViewById(R.id.titleDivider);
mButton1=(Button)mDialogView.findViewById(R.id.button1);
mButton2=(Button)mDialogView.findViewById(R.id.button2);
setContentView(mDialogView);
this.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
mLinearLayoutView.setVisibility(View.VISIBLE);
if(type==null){
type=Effectstype.slidetop;
}
start(type);
}
});
mRelativeLayoutView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isCancelable)dismiss();
}
});
}
public void toDefault(){
mTitle.setTextColor(Color.parseColor(defTextColor));
mDivider.setBackgroundColor(Color.parseColor(defDividerColor));
mMessage.setTextColor(Color.parseColor(defMsgColor));
mLinearLayoutView.setBackgroundColor(Color.parseColor(defDialogColor));
}
public NiftyDialogBuilder withDividerColor(String colorString) {
mDivider.setBackgroundColor(Color.parseColor(colorString));
return this;
}
public NiftyDialogBuilder withDividerColor(int color) {
mDivider.setBackgroundColor(color);
return this;
}
public NiftyDialogBuilder withTitle(CharSequence title) {
toggleView(mLinearLayoutTopView,title);
mTitle.setText(title);
return this;
}
public NiftyDialogBuilder withTitleColor(String colorString) {
mTitle.setTextColor(Color.parseColor(colorString));
return this;
}
public NiftyDialogBuilder withTitleColor(int color) {
mTitle.setTextColor(color);
return this;
}
public NiftyDialogBuilder withMessage(int textResId) {
toggleView(mLinearLayoutMsgView,textResId);
mMessage.setText(textResId);
return this;
}
public NiftyDialogBuilder withMessage(CharSequence msg) {
toggleView(mLinearLayoutMsgView,msg);
mMessage.setText(msg);
return this;
}
public NiftyDialogBuilder withMessageColor(String colorString) {
mMessage.setTextColor(Color.parseColor(colorString));
return this;
}
public NiftyDialogBuilder withMessageColor(int color) {
mMessage.setTextColor(color);
return this;
}
public NiftyDialogBuilder withDialogColor(String colorString) {
mLinearLayoutView.getBackground().setColorFilter(ColorUtils.getColorFilter(Color.parseColor(colorString)));
return this;
}
public NiftyDialogBuilder withDialogColor(int color) {
mLinearLayoutView.getBackground().setColorFilter(ColorUtils.getColorFilter(color));
return this;
}
public NiftyDialogBuilder withIcon(int drawableResId) {
mIcon.setImageResource(drawableResId);
return this;
}
public NiftyDialogBuilder withIcon(Drawable icon) {
mIcon.setImageDrawable(icon);
return this;
}
public NiftyDialogBuilder withDuration(int duration) {
this.mDuration=duration;
return this;
}
public NiftyDialogBuilder withEffect(Effectstype type) {
this.type=type;
return this;
}
public NiftyDialogBuilder withButtonDrawable(int resid) {
mButton1.setBackgroundResource(resid);
mButton2.setBackgroundResource(resid);
return this;
}
public NiftyDialogBuilder withButton1Text(CharSequence text) {
mButton1.setVisibility(View.VISIBLE);
mButton1.setText(text);
return this;
}
public NiftyDialogBuilder withButton2Text(CharSequence text) {
mButton2.setVisibility(View.VISIBLE);
mButton2.setText(text);
return this;
}
public NiftyDialogBuilder setButton1Click(View.OnClickListener click) {
mButton1.setOnClickListener(click);
return this;
}
public NiftyDialogBuilder setButton2Click(View.OnClickListener click) {
mButton2.setOnClickListener(click);
return this;
}
public NiftyDialogBuilder setCustomView(int resId, Context context) {
View customView = View.inflate(context, resId, null);
if (mFrameLayoutCustomView.getChildCount()>0){
mFrameLayoutCustomView.removeAllViews();
}
mFrameLayoutCustomView.addView(customView);
return this;
}
public NiftyDialogBuilder setCustomView(View view, Context context) {
if (mFrameLayoutCustomView.getChildCount()>0){
mFrameLayoutCustomView.removeAllViews();
}
mFrameLayoutCustomView.addView(view);
return this;
}
public NiftyDialogBuilder removeAllCustomView(){
if (mFrameLayoutCustomView.getChildCount()>0){
mFrameLayoutCustomView.removeAllViews();
}
return this;
}
public NiftyDialogBuilder isCancelableOnTouchOutside(boolean cancelable) {
this.isCancelable=cancelable;
this.setCanceledOnTouchOutside(cancelable);
return this;
}
public NiftyDialogBuilder isCancelable(boolean cancelable) {
this.isCancelable=cancelable;
this.setCancelable(cancelable);
return this;
}
private void toggleView(View view, Object obj){
if (obj==null){
view.setVisibility(View.GONE);
}else {
view.setVisibility(View.VISIBLE);
}
}
@Override
public void show() {
super.show();
}
private void start(Effectstype type){
BaseEffects animator = type.getAnimator();
if(mDuration != -1){
animator.setDuration(Math.abs(mDuration));
}
animator.start(mRelativeLayoutView);
}
@Override
public void dismiss() {
super.dismiss();
mButton1.setVisibility(View.GONE);
mButton2.setVisibility(View.GONE);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/SignalView.java
================================================
package com.luo.bluetooth.customview.searchble;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import com.luo.bluetooth.R;
/**
* 强度指示器,等级0-4
* Created by LPQ on 2017/1/17.
*/
public class SignalView extends View {
private Paint mPaint;
private int mWidth, mHeight;
private float cx, cy, radius;
private int mNoIntensityColor, mIntensityColor;
private int mIntensity;
public SignalView(Context context) {
this(context, null);
}
public SignalView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SignalView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignalView, defStyleAttr, 0);
mIntensityColor = typedArray.getColor(R.styleable.SignalView_intensityColor, 0xff6cb1f2);
mNoIntensityColor = typedArray.getColor(R.styleable.SignalView_noIntensityColor, 0xffbdc3c7);
typedArray.recycle();
init();
}
private void init(){
// setWillNotDraw(false);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
/**
* 设置强度
* */
public void setIntensity(int intensity){
mIntensity = intensity;
if (intensity>4)
mIntensity = 4;
if (intensity<0)
mIntensity = 0;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
cx = mWidth/2.f;
cy = mHeight*0.7f;
if (mWidth>mHeight){
radius = mHeight/12.f;
}else {
radius = mWidth/12.f;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mIntensity>0?mIntensityColor:mNoIntensityColor);
canvas.drawCircle(cx, cy, radius, mPaint);
RectF rectF = new RectF(cx-2.5f*radius, cy-2.f*radius, cx+2.5f*radius, cy+4*radius);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(radius*0.8f);
mPaint.setColor(mIntensity>1?mIntensityColor:mNoIntensityColor);
canvas.drawArc(rectF, 220, 100, false, mPaint);
float offest = 1.5f*radius;
for (int i=2;i<4;i++){
mPaint.setColor(mIntensity>i?mIntensityColor:mNoIntensityColor);
rectF.left -= offest;
rectF.top -= offest;
rectF.right += offest;
canvas.drawArc(rectF, 220, 100, false, mPaint);
}
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/BaseEffects.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.AnimatorSet;
import android.os.Build;
import android.view.View;
/**
* Modify by lee on 2014/7/30.
* https://github.com/sd6352051/NiftyDialogEffects
*
* The MIT License (MIT)
*
* Copyright (c) 2014 daimajia
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Acknowledgements daimajia
* https://github.com/daimajia
*/
public abstract class BaseEffects {
private static final int DURATION = 1 * 700;
protected long mDuration =DURATION ;
private AnimatorSet mAnimatorSet;
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mAnimatorSet = new AnimatorSet();
}
}
protected abstract void setupAnimation(View view);
public void start(View view) {
reset(view);
setupAnimation(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mAnimatorSet.start();
}
}
public void reset(View view) {
// ViewHelper.setPivotX(view, view.getMeasuredWidth() / 2.0f);
// ViewHelper.setPivotY(view, view.getMeasuredHeight() / 2.0f);
}
public AnimatorSet getAnimatorSet() {
return mAnimatorSet;
}
public void setDuration(long duration) {
this.mDuration = duration;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FadeIn.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class FadeIn extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Fall.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class Fall extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "scaleX", 2, 1.5f, 1).setDuration(mDuration),
ObjectAnimator.ofFloat(view,"scaleY",2,1.5f,1).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FlipH.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class FlipH extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotationY", -90, 0).setDuration(mDuration)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FlipV.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class FlipV extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotationX", -90, 0).setDuration(mDuration)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/NewsPaper.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class NewsPaper extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotation", 1080, 720, 360, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2),
ObjectAnimator.ofFloat(view, "scaleX", 0.1f, 0.5f, 1).setDuration(mDuration),
ObjectAnimator.ofFloat(view,"scaleY",0.1f,0.5f,1).setDuration(mDuration)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/RotateBottom.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class RotateBottom extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotationX",90, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "translationY", 300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/RotateLeft.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class RotateLeft extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotationY", 90, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "translationX", -300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Shake.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.os.Build;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class Shake extends BaseEffects{
@Override
protected void setupAnimation(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "translationX", 0, .10f, -25, .26f, 25, .42f, -25, .58f, 25, .74f, -25, .90f, 1, 0).setDuration(mDuration)
);
}
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SideFall.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SideFall extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "scaleX", 2, 1.5f, 1).setDuration(mDuration),
ObjectAnimator.ofFloat(view,"scaleY",2,1.5f,1).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "rotation", 25,0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "translationX",80,0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideBottom.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.os.Build;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SlideBottom extends BaseEffects{
@Override
protected void setupAnimation(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "translationY", 300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideLeft.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SlideLeft extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "translationX", -300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideRight.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SlideRight extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "translationX", 300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideTop.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class SlideTop extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "translationY", -300, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0, 1).setDuration(mDuration*3/2)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Slit.java
================================================
package com.luo.bluetooth.customview.searchble.effects;
import android.animation.ObjectAnimator;
import android.view.View;
/*
* Copyright 2014 litao
* https://github.com/sd6352051/NiftyDialogEffects
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class Slit extends BaseEffects{
@Override
protected void setupAnimation(View view) {
getAnimatorSet().playTogether(
ObjectAnimator.ofFloat(view, "rotationY", 90, 88, 88, 45, 0).setDuration(mDuration),
ObjectAnimator.ofFloat(view, "alpha", 0,0.4f,0.8f, 1).setDuration(mDuration*3/2),
ObjectAnimator.ofFloat(view, "scaleX", 0,0.5f, 0.9f, 0.9f, 1).setDuration(mDuration),
ObjectAnimator.ofFloat(view,"scaleY",0,0.5f, 0.9f, 0.9f, 1).setDuration(mDuration)
);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/encryption/Aes.java
================================================
/*
* Copyright (c) 2019. stag All rights reserved.
*/
package com.luo.bluetooth.encryption;
import com.stag.bluetooth.util.ByteUtils;
/**
* AES加密
*/
public class Aes {
// foreward sbox
private static final byte sbox[] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
(byte) 0x63, (byte) 0x7c, (byte) 0x77, (byte) 0x7b, (byte) 0xf2, (byte) 0x6b, (byte) 0x6f, (byte) 0xc5, (byte) 0x30, (byte) 0x01, (byte) 0x67, (byte) 0x2b, (byte) 0xfe, (byte) 0xd7, (byte) 0xab, (byte) 0x76, //0
(byte) 0xca, (byte) 0x82, (byte) 0xc9, (byte) 0x7d, (byte) 0xfa, (byte) 0x59, (byte) 0x47, (byte) 0xf0, (byte) 0xad, (byte) 0xd4, (byte) 0xa2, (byte) 0xaf, (byte) 0x9c, (byte) 0xa4, (byte) 0x72, (byte) 0xc0, //1
(byte) 0xb7, (byte) 0xfd, (byte) 0x93, (byte) 0x26, (byte) 0x36, (byte) 0x3f, (byte) 0xf7, (byte) 0xcc, (byte) 0x34, (byte) 0xa5, (byte) 0xe5, (byte) 0xf1, (byte) 0x71, (byte) 0xd8, (byte) 0x31, (byte) 0x15, //2
(byte) 0x04, (byte) 0xc7, (byte) 0x23, (byte) 0xc3, (byte) 0x18, (byte) 0x96, (byte) 0x05, (byte) 0x9a, (byte) 0x07, (byte) 0x12, (byte) 0x80, (byte) 0xe2, (byte) 0xeb, (byte) 0x27, (byte) 0xb2, (byte) 0x75, //3
(byte) 0x09, (byte) 0x83, (byte) 0x2c, (byte) 0x1a, (byte) 0x1b, (byte) 0x6e, (byte) 0x5a, (byte) 0xa0, (byte) 0x52, (byte) 0x3b, (byte) 0xd6, (byte) 0xb3, (byte) 0x29, (byte) 0xe3, (byte) 0x2f, (byte) 0x84, //4
(byte) 0x53, (byte) 0xd1, (byte) 0x00, (byte) 0xed, (byte) 0x20, (byte) 0xfc, (byte) 0xb1, (byte) 0x5b, (byte) 0x6a, (byte) 0xcb, (byte) 0xbe, (byte) 0x39, (byte) 0x4a, (byte) 0x4c, (byte) 0x58, (byte) 0xcf, //5
(byte) 0xd0, (byte) 0xef, (byte) 0xaa, (byte) 0xfb, (byte) 0x43, (byte) 0x4d, (byte) 0x33, (byte) 0x85, (byte) 0x45, (byte) 0xf9, (byte) 0x02, (byte) 0x7f, (byte) 0x50, (byte) 0x3c, (byte) 0x9f, (byte) 0xa8, //6
(byte) 0x51, (byte) 0xa3, (byte) 0x40, (byte) 0x8f, (byte) 0x92, (byte) 0x9d, (byte) 0x38, (byte) 0xf5, (byte) 0xbc, (byte) 0xb6, (byte) 0xda, (byte) 0x21, (byte) 0x10, (byte) 0xff, (byte) 0xf3, (byte) 0xd2, //7
(byte) 0xcd, (byte) 0x0c, (byte) 0x13, (byte) 0xec, (byte) 0x5f, (byte) 0x97, (byte) 0x44, (byte) 0x17, (byte) 0xc4, (byte) 0xa7, (byte) 0x7e, (byte) 0x3d, (byte) 0x64, (byte) 0x5d, (byte) 0x19, (byte) 0x73, //8
(byte) 0x60, (byte) 0x81, (byte) 0x4f, (byte) 0xdc, (byte) 0x22, (byte) 0x2a, (byte) 0x90, (byte) 0x88, (byte) 0x46, (byte) 0xee, (byte) 0xb8, (byte) 0x14, (byte) 0xde, (byte) 0x5e, (byte) 0x0b, (byte) 0xdb, //9
(byte) 0xe0, (byte) 0x32, (byte) 0x3a, (byte) 0x0a, (byte) 0x49, (byte) 0x06, (byte) 0x24, (byte) 0x5c, (byte) 0xc2, (byte) 0xd3, (byte) 0xac, (byte) 0x62, (byte) 0x91, (byte) 0x95, (byte) 0xe4, (byte) 0x79, //A
(byte) 0xe7, (byte) 0xc8, (byte) 0x37, (byte) 0x6d, (byte) 0x8d, (byte) 0xd5, (byte) 0x4e, (byte) 0xa9, (byte) 0x6c, (byte) 0x56, (byte) 0xf4, (byte) 0xea, (byte) 0x65, (byte) 0x7a, (byte) 0xae, (byte) 0x08, //B
(byte) 0xba, (byte) 0x78, (byte) 0x25, (byte) 0x2e, (byte) 0x1c, (byte) 0xa6, (byte) 0xb4, (byte) 0xc6, (byte) 0xe8, (byte) 0xdd, (byte) 0x74, (byte) 0x1f, (byte) 0x4b, (byte) 0xbd, (byte) 0x8b, (byte) 0x8a, //C
(byte) 0x70, (byte) 0x3e, (byte) 0xb5, (byte) 0x66, (byte) 0x48, (byte) 0x03, (byte) 0xf6, (byte) 0x0e, (byte) 0x61, (byte) 0x35, (byte) 0x57, (byte) 0xb9, (byte) 0x86, (byte) 0xc1, (byte) 0x1d, (byte) 0x9e, //D
(byte) 0xe1, (byte) 0xf8, (byte) 0x98, (byte) 0x11, (byte) 0x69, (byte) 0xd9, (byte) 0x8e, (byte) 0x94, (byte) 0x9b, (byte) 0x1e, (byte) 0x87, (byte) 0xe9, (byte) 0xce, (byte) 0x55, (byte) 0x28, (byte) 0xdf, //E
(byte) 0x8c, (byte) 0xa1, (byte) 0x89, (byte) 0x0d, (byte) 0xbf, (byte) 0xe6, (byte) 0x42, (byte) 0x68, (byte) 0x41, (byte) 0x99, (byte) 0x2d, (byte) 0x0f, (byte) 0xb0, (byte) 0x54, (byte) 0xbb, (byte) 0x16}; //F
// inverse sbox
private static final byte rsbox[] = {
(byte) 0x52, (byte) 0x09, (byte) 0x6a, (byte) 0xd5, (byte) 0x30, (byte) 0x36, (byte) 0xa5, (byte) 0x38, (byte) 0xbf, (byte) 0x40, (byte) 0xa3, (byte) 0x9e, (byte) 0x81, (byte) 0xf3, (byte) 0xd7, (byte) 0xfb,
(byte) 0x7c, (byte) 0xe3, (byte) 0x39, (byte) 0x82, (byte) 0x9b, (byte) 0x2f, (byte) 0xff, (byte) 0x87, (byte) 0x34, (byte) 0x8e, (byte) 0x43, (byte) 0x44, (byte) 0xc4, (byte) 0xde, (byte) 0xe9, (byte) 0xcb,
(byte) 0x54, (byte) 0x7b, (byte) 0x94, (byte) 0x32, (byte) 0xa6, (byte) 0xc2, (byte) 0x23, (byte) 0x3d, (byte) 0xee, (byte) 0x4c, (byte) 0x95, (byte) 0x0b, (byte) 0x42, (byte) 0xfa, (byte) 0xc3, (byte) 0x4e,
(byte) 0x08, (byte) 0x2e, (byte) 0xa1, (byte) 0x66, (byte) 0x28, (byte) 0xd9, (byte) 0x24, (byte) 0xb2, (byte) 0x76, (byte) 0x5b, (byte) 0xa2, (byte) 0x49, (byte) 0x6d, (byte) 0x8b, (byte) 0xd1, (byte) 0x25,
(byte) 0x72, (byte) 0xf8, (byte) 0xf6, (byte) 0x64, (byte) 0x86, (byte) 0x68, (byte) 0x98, (byte) 0x16, (byte) 0xd4, (byte) 0xa4, (byte) 0x5c, (byte) 0xcc, (byte) 0x5d, (byte) 0x65, (byte) 0xb6, (byte) 0x92,
(byte) 0x6c, (byte) 0x70, (byte) 0x48, (byte) 0x50, (byte) 0xfd, (byte) 0xed, (byte) 0xb9, (byte) 0xda, (byte) 0x5e, (byte) 0x15, (byte) 0x46, (byte) 0x57, (byte) 0xa7, (byte) 0x8d, (byte) 0x9d, (byte) 0x84,
(byte) 0x90, (byte) 0xd8, (byte) 0xab, (byte) 0x00, (byte) 0x8c, (byte) 0xbc, (byte) 0xd3, (byte) 0x0a, (byte) 0xf7, (byte) 0xe4, (byte) 0x58, (byte) 0x05, (byte) 0xb8, (byte) 0xb3, (byte) 0x45, (byte) 0x06,
(byte) 0xd0, (byte) 0x2c, (byte) 0x1e, (byte) 0x8f, (byte) 0xca, (byte) 0x3f, (byte) 0x0f, (byte) 0x02, (byte) 0xc1, (byte) 0xaf, (byte) 0xbd, (byte) 0x03, (byte) 0x01, (byte) 0x13, (byte) 0x8a, (byte) 0x6b,
(byte) 0x3a, (byte) 0x91, (byte) 0x11, (byte) 0x41, (byte) 0x4f, (byte) 0x67, (byte) 0xdc, (byte) 0xea, (byte) 0x97, (byte) 0xf2, (byte) 0xcf, (byte) 0xce, (byte) 0xf0, (byte) 0xb4, (byte) 0xe6, (byte) 0x73,
(byte) 0x96, (byte) 0xac, (byte) 0x74, (byte) 0x22, (byte) 0xe7, (byte) 0xad, (byte) 0x35, (byte) 0x85, (byte) 0xe2, (byte) 0xf9, (byte) 0x37, (byte) 0xe8, (byte) 0x1c, (byte) 0x75, (byte) 0xdf, (byte) 0x6e,
(byte) 0x47, (byte) 0xf1, (byte) 0x1a, (byte) 0x71, (byte) 0x1d, (byte) 0x29, (byte) 0xc5, (byte) 0x89, (byte) 0x6f, (byte) 0xb7, (byte) 0x62, (byte) 0x0e, (byte) 0xaa, (byte) 0x18, (byte) 0xbe, (byte) 0x1b,
(byte) 0xfc, (byte) 0x56, (byte) 0x3e, (byte) 0x4b, (byte) 0xc6, (byte) 0xd2, (byte) 0x79, (byte) 0x20, (byte) 0x9a, (byte) 0xdb, (byte) 0xc0, (byte) 0xfe, (byte) 0x78, (byte) 0xcd, (byte) 0x5a, (byte) 0xf4,
(byte) 0x1f, (byte) 0xdd, (byte) 0xa8, (byte) 0x33, (byte) 0x88, (byte) 0x07, (byte) 0xc7, (byte) 0x31, (byte) 0xb1, (byte) 0x12, (byte) 0x10, (byte) 0x59, (byte) 0x27, (byte) 0x80, (byte) 0xec, (byte) 0x5f,
(byte) 0x60, (byte) 0x51, (byte) 0x7f, (byte) 0xa9, (byte) 0x19, (byte) 0xb5, (byte) 0x4a, (byte) 0x0d, (byte) 0x2d, (byte) 0xe5, (byte) 0x7a, (byte) 0x9f, (byte) 0x93, (byte) 0xc9, (byte) 0x9c, (byte) 0xef,
(byte) 0xa0, (byte) 0xe0, (byte) 0x3b, (byte) 0x4d, (byte) 0xae, (byte) 0x2a, (byte) 0xf5, (byte) 0xb0, (byte) 0xc8, (byte) 0xeb, (byte) 0xbb, (byte) 0x3c, (byte) 0x83, (byte) 0x53, (byte) 0x99, (byte) 0x61,
(byte) 0x17, (byte) 0x2b, (byte) 0x04, (byte) 0x7e, (byte) 0xba, (byte) 0x77, (byte) 0xd6, (byte) 0x26, (byte) 0xe1, (byte) 0x69, (byte) 0x14, (byte) 0x63, (byte) 0x55, (byte) 0x21, (byte) 0x0c, (byte) 0x7d};
// round constant
private static final byte Rcon[] = {
(byte) 0x8d, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x08, (byte) 0x10, (byte) 0x20, (byte) 0x40, (byte) 0x80, (byte) 0x1b, (byte) 0x36};
// expand the key
private static byte[] expandKey(byte[] key) {
byte[] expandedKey = new byte[176];
int ii, buf1;
for (ii = 0; ii < 16; ii++)
expandedKey[ii] = key[ii];
for (ii = 1; ii < 11; ii++) {
buf1 = expandedKey[ii * 16 - 4];
expandedKey[ii * 16 + 0] = (byte) ((sbox[expandedKey[ii * 16 - 3] & 0xff] ^ expandedKey[(ii - 1) * 16 + 0] ^ Rcon[ii]) & 0xff);
expandedKey[ii * 16 + 1] = (byte) ((sbox[expandedKey[ii * 16 - 2] & 0xff] ^ expandedKey[(ii - 1) * 16 + 1]) & 0xff);
expandedKey[ii * 16 + 2] = (byte) ((sbox[expandedKey[ii * 16 - 1] & 0xff] ^ expandedKey[(ii - 1) * 16 + 2]) & 0xff);
expandedKey[ii * 16 + 3] = (byte) ((sbox[buf1 & 0xff] ^ expandedKey[(ii - 1) * 16 + 3]) & 0xff);
expandedKey[ii * 16 + 4] = (byte) ((expandedKey[(ii - 1) * 16 + 4] ^ expandedKey[ii * 16 + 0]) & 0xff);
expandedKey[ii * 16 + 5] = (byte) ((expandedKey[(ii - 1) * 16 + 5] ^ expandedKey[ii * 16 + 1]) & 0xff);
expandedKey[ii * 16 + 6] = (byte) ((expandedKey[(ii - 1) * 16 + 6] ^ expandedKey[ii * 16 + 2]) & 0xff);
expandedKey[ii * 16 + 7] = (byte) ((expandedKey[(ii - 1) * 16 + 7] ^ expandedKey[ii * 16 + 3]) & 0xff);
expandedKey[ii * 16 + 8] = (byte) ((expandedKey[(ii - 1) * 16 + 8] ^ expandedKey[ii * 16 + 4]) & 0xff);
expandedKey[ii * 16 + 9] = (byte) ((expandedKey[(ii - 1) * 16 + 9] ^ expandedKey[ii * 16 + 5]) & 0xff);
expandedKey[ii * 16 + 10] = (byte) ((expandedKey[(ii - 1) * 16 + 10] ^ expandedKey[ii * 16 + 6]) & 0xff);
expandedKey[ii * 16 + 11] = (byte) ((expandedKey[(ii - 1) * 16 + 11] ^ expandedKey[ii * 16 + 7]) & 0xff);
expandedKey[ii * 16 + 12] = (byte) ((expandedKey[(ii - 1) * 16 + 12] ^ expandedKey[ii * 16 + 8]) & 0xff);
expandedKey[ii * 16 + 13] = (byte) ((expandedKey[(ii - 1) * 16 + 13] ^ expandedKey[ii * 16 + 9]) & 0xff);
expandedKey[ii * 16 + 14] = (byte) ((expandedKey[(ii - 1) * 16 + 14] ^ expandedKey[ii * 16 + 10]) & 0xff);
expandedKey[ii * 16 + 15] = (byte) ((expandedKey[(ii - 1) * 16 + 15] ^ expandedKey[ii * 16 + 11]) & 0xff);
}
return expandedKey;
}
// multiply by 2 in the galois field
private static byte galois_mul2(byte value) {
if (value >> 7 != 0x00) {
value = (byte) ((value << 1) & 0xff);
return (byte) ((value ^ 0x1b) & 0xff);
} else {
return (byte) ((value << 1) & 0xff);
}
}
// straight foreward aes encryption implementation
// first the group of operations
// - addroundkey
// - subbytes
// - shiftrows
// - mixcolums
// is executed 9 times, after this addroundkey to finish the 9th round,
// after that the 10th round without mixcolums
// no further subfunctions to save cycles for function calls
// no structuring with "for (....)" to save cycles
private static byte[] aes_encr(byte[] input, byte[] expandedKey) {
byte buf1, buf2, buf3, round;
byte[] state = new byte[input.length];
for (int i = 0; i < input.length; i++) {
state[i] = input[i];
}
for (round = 0; round < 9; round++) {
// addroundkey, sbox and shiftrows
// row 0
state[0] = sbox[(state[0] ^ expandedKey[(round * 16)]) & 0xff];
state[4] = sbox[(state[4] ^ expandedKey[(round * 16) + 4]) & 0xff];
state[8] = sbox[(state[8] ^ expandedKey[(round * 16) + 8]) & 0xff];
state[12] = sbox[(state[12] ^ expandedKey[(round * 16) + 12]) & 0xff];
// row 1
buf1 = (byte) ((state[1] ^ expandedKey[(round * 16) + 1]) & 0xff);
state[1] = sbox[(state[5] ^ expandedKey[(round * 16) + 5]) & 0xff];
state[5] = sbox[(state[9] ^ expandedKey[(round * 16) + 9]) & 0xff];
state[9] = sbox[(state[13] ^ expandedKey[(round * 16) + 13]) & 0xff];
state[13] = sbox[buf1 & 0xff];
// row 2
buf1 = (byte) ((state[2] ^ expandedKey[(round * 16) + 2]) & 0xff);
buf2 = (byte) ((state[6] ^ expandedKey[(round * 16) + 6]) & 0xff);
state[2] = sbox[(state[10] ^ expandedKey[(round * 16) + 10]) & 0xff];
state[6] = sbox[(state[14] ^ expandedKey[(round * 16) + 14]) & 0xff];
state[10] = sbox[buf1 & 0xff];
state[14] = sbox[buf2 & 0xff];
// row 3
buf1 = (byte) ((state[15] ^ expandedKey[(round * 16) + 15]) & 0xff);
state[15] = sbox[(state[11] ^ expandedKey[(round * 16) + 11]) & 0xff];
state[11] = sbox[(state[7] ^ expandedKey[(round * 16) + 7]) & 0xff];
state[7] = sbox[(state[3] ^ expandedKey[(round * 16) + 3]) & 0xff];
state[3] = sbox[buf1 & 0xff];
// mixcolums //////////
// col1
buf1 = (byte) ((state[0] ^ state[1] ^ state[2] ^ state[3]) & 0xff);
buf2 = state[0];
buf3 = (byte) ((state[0] ^ state[1]) & 0xff);
buf3 = galois_mul2(buf3);
state[0] = (byte) ((state[0] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[1] ^ state[2]) & 0xff);
buf3 = galois_mul2(buf3);
state[1] = (byte) ((state[1] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[2] ^ state[3]) & 0xff);
buf3 = galois_mul2(buf3);
state[2] = (byte) ((state[2] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[3] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[3] = (byte) ((state[3] ^ buf3 ^ buf1) & 0xff);
// col2
buf1 = (byte) ((state[4] ^ state[5] ^ state[6] ^ state[7]) & 0xff);
buf2 = state[4];
buf3 = (byte) ((state[4] ^ state[5]) & 0xff);
buf3 = galois_mul2(buf3);
state[4] = (byte) ((state[4] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[5] ^ state[6]) & 0xff);
buf3 = galois_mul2(buf3);
state[5] = (byte) ((state[5] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[6] ^ state[7]) & 0xff);
buf3 = galois_mul2(buf3);
state[6] = (byte) ((state[6] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[7] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[7] = (byte) ((state[7] ^ buf3 ^ buf1) & 0xff);
// col3
buf1 = (byte) ((state[8] ^ state[9] ^ state[10] ^ state[11]) & 0xff);
buf2 = state[8];
buf3 = (byte) ((state[8] ^ state[9]) & 0xff);
buf3 = galois_mul2(buf3);
state[8] = (byte) ((state[8] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[9] ^ state[10]) & 0xff);
buf3 = galois_mul2(buf3);
state[9] = (byte) ((state[9] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[10] ^ state[11]) & 0xff);
buf3 = galois_mul2(buf3);
state[10] = (byte) ((state[10] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[11] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[11] = (byte) ((state[11] ^ buf3 ^ buf1) & 0xff);
// col4
buf1 = (byte) ((state[12] ^ state[13] ^ state[14] ^ state[15]) & 0xff);
buf2 = state[12];
buf3 = (byte) ((state[12] ^ state[13]) & 0xff);
buf3 = galois_mul2(buf3);
state[12] = (byte) ((state[12] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[13] ^ state[14]) & 0xff);
buf3 = galois_mul2(buf3);
state[13] = (byte) ((state[13] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[14] ^ state[15]) & 0xff);
buf3 = galois_mul2(buf3);
state[14] = (byte) ((state[14] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[15] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[15] = (byte) ((state[15] ^ buf3 ^ buf1) & 0xff);
}
// 10th round without mixcols
state[0] = sbox[(state[0] ^ expandedKey[(round * 16)]) & 0xff];
state[4] = sbox[(state[4] ^ expandedKey[(round * 16) + 4]) & 0xff];
state[8] = sbox[(state[8] ^ expandedKey[(round * 16) + 8]) & 0xff];
state[12] = sbox[(state[12] ^ expandedKey[(round * 16) + 12]) & 0xff];
// row 1
buf1 = (byte) ((state[1] ^ expandedKey[(round * 16) + 1]) & 0xff);
state[1] = sbox[(state[5] ^ expandedKey[(round * 16) + 5]) & 0xff];
state[5] = sbox[(state[9] ^ expandedKey[(round * 16) + 9]) & 0xff];
state[9] = sbox[(state[13] ^ expandedKey[(round * 16) + 13]) & 0xff];
state[13] = sbox[buf1 & 0xff];
// row 2
buf1 = (byte) ((state[2] ^ expandedKey[(round * 16) + 2]) & 0xff);
buf2 = (byte) ((state[6] ^ expandedKey[(round * 16) + 6]) & 0xff);
state[2] = sbox[(state[10] ^ expandedKey[(round * 16) + 10]) & 0xff];
state[6] = sbox[(state[14] ^ expandedKey[(round * 16) + 14]) & 0xff];
state[10] = sbox[buf1 & 0xff];
state[14] = sbox[buf2 & 0xff];
// row 3
buf1 = (byte) ((state[15] ^ expandedKey[(round * 16) + 15]) & 0xff);
state[15] = sbox[(state[11] ^ expandedKey[(round * 16) + 11]) & 0xff];
state[11] = sbox[(state[7] ^ expandedKey[(round * 16) + 7]) & 0xff];
state[7] = sbox[(state[3] ^ expandedKey[(round * 16) + 3]) & 0xff];
state[3] = sbox[buf1 & 0xff];
// last addroundkey
state[0] ^= expandedKey[160];
state[1] ^= expandedKey[161];
state[2] ^= expandedKey[162];
state[3] ^= expandedKey[163];
state[4] ^= expandedKey[164];
state[5] ^= expandedKey[165];
state[6] ^= expandedKey[166];
state[7] ^= expandedKey[167];
state[8] ^= expandedKey[168];
state[9] ^= expandedKey[169];
state[10] ^= expandedKey[170];
state[11] ^= expandedKey[171];
state[12] ^= expandedKey[172];
state[13] ^= expandedKey[173];
state[14] ^= expandedKey[174];
state[15] ^= expandedKey[175];
return state;
}
// straight foreward aes decryption implementation
// the order of substeps is the exact reverse of decryption
// inverse functions:
// - addRoundKey is its own inverse
// - rsbox is inverse of sbox
// - rightshift instead of leftshift
// - invMixColumns = barreto + mixColumns
// no further subfunctions to save cycles for function calls
// no structuring with "for (....)" to save cycles
private static byte[] aes_decr(byte[] input, byte[] expandedKey) {
byte buf1, buf2, buf3;
byte round;
round = 9;
byte[] state = new byte[input.length];
for (int i = 0; i < input.length; i++) {
state[i] = input[i];
}
// initial addroundkey
state[0] ^= expandedKey[160];
state[1] ^= expandedKey[161];
state[2] ^= expandedKey[162];
state[3] ^= expandedKey[163];
state[4] ^= expandedKey[164];
state[5] ^= expandedKey[165];
state[6] ^= expandedKey[166];
state[7] ^= expandedKey[167];
state[8] ^= expandedKey[168];
state[9] ^= expandedKey[169];
state[10] ^= expandedKey[170];
state[11] ^= expandedKey[171];
state[12] ^= expandedKey[172];
state[13] ^= expandedKey[173];
state[14] ^= expandedKey[174];
state[15] ^= expandedKey[175];
// 10th round without mixcols
state[0] = (byte) ((rsbox[state[0] & 0xff] ^ expandedKey[(round * 16)]) & 0xff);
state[4] = (byte) ((rsbox[state[4] & 0xff] ^ expandedKey[(round * 16) + 4]) & 0xff);
state[8] = (byte) ((rsbox[state[8] & 0xff] ^ expandedKey[(round * 16) + 8]) & 0xff);
state[12] = (byte) ((rsbox[state[12] & 0xff] ^ expandedKey[(round * 16) + 12]) & 0xff);
// row 1
buf1 = (byte) ((rsbox[state[13] & 0xff] ^ expandedKey[(round * 16) + 1]) & 0xff);
state[13] = (byte) ((rsbox[state[9] & 0xff] ^ expandedKey[(round * 16) + 13]) & 0xff);
state[9] = (byte) ((rsbox[state[5] & 0xff] ^ expandedKey[(round * 16) + 9]) & 0xff);
state[5] = (byte) ((rsbox[state[1] & 0xff] ^ expandedKey[(round * 16) + 5]) & 0xff);
state[1] = buf1;
// row 2
buf1 = (byte) ((rsbox[state[2] & 0xff] ^ expandedKey[(round * 16) + 10]) & 0xff);
buf2 = (byte) ((rsbox[state[6] & 0xff] ^ expandedKey[(round * 16) + 14]) & 0xff);
state[2] = (byte) ((rsbox[state[10] & 0xff] ^ expandedKey[(round * 16) + 2]) & 0xff);
state[6] = (byte) ((rsbox[state[14] & 0xff] ^ expandedKey[(round * 16) + 6]) & 0xff);
state[10] = buf1;
state[14] = buf2;
// row 3
buf1 = (byte) ((rsbox[state[3] & 0xff] ^ expandedKey[(round * 16) + 15]) & 0xff);
state[3] = (byte) ((rsbox[state[7] & 0xff] ^ expandedKey[(round * 16) + 3]) & 0xff);
state[7] = (byte) ((rsbox[state[11] & 0xff] ^ expandedKey[(round * 16) + 7]) & 0xff);
state[11] = (byte) ((rsbox[state[15] & 0xff] ^ expandedKey[(round * 16) + 11]) & 0xff);
state[15] = buf1;
for (round = 8; round >= 0; round--) {
// barreto
//col1
buf1 = galois_mul2(galois_mul2((byte) ((state[0] ^ state[2]) & 0xff)));
buf2 = galois_mul2(galois_mul2((byte) ((state[1] ^ state[3]) & 0xff)));
state[0] ^= buf1;
state[1] ^= buf2;
state[2] ^= buf1;
state[3] ^= buf2;
//col2
buf1 = galois_mul2(galois_mul2((byte) ((state[4] ^ state[6]) & 0xff)));
buf2 = galois_mul2(galois_mul2((byte) ((state[5] ^ state[7]) & 0xff)));
state[4] ^= buf1;
state[5] ^= buf2;
state[6] ^= buf1;
state[7] ^= buf2;
//col3
buf1 = galois_mul2(galois_mul2((byte) ((state[8] ^ state[10]) & 0xff)));
buf2 = galois_mul2(galois_mul2((byte) ((state[9] ^ state[11]) & 0xff)));
state[8] ^= buf1;
state[9] ^= buf2;
state[10] ^= buf1;
state[11] ^= buf2;
//col4
buf1 = galois_mul2(galois_mul2((byte) ((state[12] ^ state[14]) & 0xff)));
buf2 = galois_mul2(galois_mul2((byte) ((state[13] ^ state[15]) & 0xff)));
state[12] ^= buf1;
state[13] ^= buf2;
state[14] ^= buf1;
state[15] ^= buf2;
// mixcolums //////////
// col1
buf1 = (byte) ((state[0] ^ state[1] ^ state[2] ^ state[3]) & 0xff);
buf2 = state[0];
buf3 = (byte) ((state[0] ^ state[1]) & 0xff);
buf3 = galois_mul2(buf3);
state[0] = (byte) ((state[0] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[1] ^ state[2]) & 0xff);
buf3 = galois_mul2(buf3);
state[1] = (byte) ((state[1] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[2] ^ state[3]) & 0xff);
buf3 = galois_mul2(buf3);
state[2] = (byte) ((state[2] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[3] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[3] = (byte) ((state[3] ^ buf3 ^ buf1) & 0xff);
// col2
buf1 = (byte) ((state[4] ^ state[5] ^ state[6] ^ state[7]) & 0xff);
buf2 = state[4];
buf3 = (byte) ((state[4] ^ state[5]) & 0xff);
buf3 = galois_mul2(buf3);
state[4] = (byte) ((state[4] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[5] ^ state[6]) & 0xff);
buf3 = galois_mul2(buf3);
state[5] = (byte) ((state[5] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[6] ^ state[7]) & 0xff);
buf3 = galois_mul2(buf3);
state[6] = (byte) ((state[6] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[7] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[7] = (byte) ((state[7] ^ buf3 ^ buf1) & 0xff);
// col3
buf1 = (byte) ((state[8] ^ state[9] ^ state[10] ^ state[11]) & 0xff);
buf2 = state[8];
buf3 = (byte) ((state[8] ^ state[9]) & 0xff);
buf3 = galois_mul2(buf3);
state[8] = (byte) ((state[8] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[9] ^ state[10]) & 0xff);
buf3 = galois_mul2(buf3);
state[9] = (byte) ((state[9] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[10] ^ state[11]) & 0xff);
buf3 = galois_mul2(buf3);
state[10] = (byte) ((state[10] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[11] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[11] = (byte) ((state[11] ^ buf3 ^ buf1) & 0xff);
// col4
buf1 = (byte) ((state[12] ^ state[13] ^ state[14] ^ state[15]) & 0xff);
buf2 = state[12];
buf3 = (byte) ((state[12] ^ state[13]) & 0xff);
buf3 = galois_mul2(buf3);
state[12] = (byte) ((state[12] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[13] ^ state[14]) & 0xff);
buf3 = galois_mul2(buf3);
state[13] = (byte) ((state[13] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[14] ^ state[15]) & 0xff);
buf3 = galois_mul2(buf3);
state[14] = (byte) ((state[14] ^ buf3 ^ buf1) & 0xff);
buf3 = (byte) ((state[15] ^ buf2) & 0xff);
buf3 = galois_mul2(buf3);
state[15] = (byte) ((state[15] ^ buf3 ^ buf1) & 0xff);
// addroundkey, rsbox and shiftrows
// row 0
state[0] = (byte) ((rsbox[state[0] & 0xff] ^ expandedKey[(round * 16)]) & 0xff);
state[4] = (byte) ((rsbox[state[4] & 0xff] ^ expandedKey[(round * 16) + 4]) & 0xff);
state[8] = (byte) ((rsbox[state[8] & 0xff] ^ expandedKey[(round * 16) + 8]) & 0xff);
state[12] = (byte) ((rsbox[state[12] & 0xff] ^ expandedKey[(round * 16) + 12]) & 0xff);
// row 1
buf1 = (byte) ((rsbox[state[13] & 0xff] ^ expandedKey[(round * 16) + 1]) & 0xff);
state[13] = (byte) ((rsbox[state[9] & 0xff] ^ expandedKey[(round * 16) + 13]) & 0xff);
state[9] = (byte) ((rsbox[state[5] & 0xff] ^ expandedKey[(round * 16) + 9]) & 0xff);
state[5] = (byte) ((rsbox[state[1] & 0xff] ^ expandedKey[(round * 16) + 5]) & 0xff);
state[1] = buf1;
// row 2
buf1 = (byte) ((rsbox[state[2] & 0xff] ^ expandedKey[(round * 16) + 10]) & 0xff);
buf2 = (byte) ((rsbox[state[6] & 0xff] ^ expandedKey[(round * 16) + 14]) & 0xff);
state[2] = (byte) ((rsbox[state[10] & 0xff] ^ expandedKey[(round * 16) + 2]) & 0xff);
state[6] = (byte) ((rsbox[state[14] & 0xff] ^ expandedKey[(round * 16) + 6]) & 0xff);
state[10] = buf1;
state[14] = buf2;
// row 3
buf1 = (byte) ((rsbox[state[3] & 0xff] ^ expandedKey[(round * 16) + 15]) & 0xff);
state[3] = (byte) ((rsbox[state[7] & 0xff] ^ expandedKey[(round * 16) + 3]) & 0xff);
state[7] = (byte) ((rsbox[state[11] & 0xff] ^ expandedKey[(round * 16) + 7]) & 0xff);
state[11] = (byte) ((rsbox[state[15] & 0xff] ^ expandedKey[(round * 16) + 11]) & 0xff);
state[15] = buf1;
}
return state;
}
public static byte[] decode16(byte[] data, byte[] key) {
byte[] expandedKey = expandKey(key);
return aes_decr(data, expandedKey);
}
public static byte[] encode16(byte[] data, byte[] key) {
byte[] expandedKey = expandKey(key);
return aes_encr(data, expandedKey);
}
public static byte[] decode(byte[] data, byte[] key) {
byte[] result = new byte[0];
for (int i = 0; i < data.length / 16; i++) {
byte[] temp = ByteUtils.subBytes(data, i * 16, 16);
result = ByteUtils.combineByteArray(result, decode16(temp, key));
}
return result;
}
public static byte[] encode(byte[] data, byte[] key) {
byte[] result = new byte[0];
for (int i = 0; i < data.length / 16; i++) {
byte[] temp = ByteUtils.subBytes(data, i * 16, 16);
result = ByteUtils.combineByteArray(result, encode16(temp, key));
}
return result;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/encryption/CRC.java
================================================
package com.luo.bluetooth.encryption;
public class CRC {
static byte[] crc8_tab = { (byte) 0, (byte) 94, (byte) 188, (byte) 226, (byte) 97, (byte) 63, (byte) 221, (byte) 131, (byte) 194, (byte) 156, (byte) 126, (byte) 32, (byte) 163, (byte) 253, (byte) 31, (byte) 65, (byte) 157, (byte) 195, (byte) 33, (byte) 127, (byte) 252, (byte) 162, (byte) 64, (byte) 30, (byte) 95, (byte) 1, (byte) 227, (byte) 189, (byte) 62, (byte) 96, (byte) 130, (byte) 220, (byte) 35, (byte) 125, (byte) 159, (byte) 193, (byte) 66, (byte) 28, (byte) 254, (byte) 160, (byte) 225, (byte) 191, (byte) 93, (byte) 3, (byte) 128, (byte) 222, (byte) 60, (byte) 98, (byte) 190, (byte) 224, (byte) 2, (byte) 92, (byte) 223, (byte) 129, (byte) 99, (byte) 61, (byte) 124, (byte) 34, (byte) 192, (byte) 158, (byte) 29, (byte) 67, (byte) 161, (byte) 255, (byte) 70, (byte) 24,
(byte) 250, (byte) 164, (byte) 39, (byte) 121, (byte) 155, (byte) 197, (byte) 132, (byte) 218, (byte) 56, (byte) 102, (byte) 229, (byte) 187, (byte) 89, (byte) 7, (byte) 219, (byte) 133, (byte) 103, (byte) 57, (byte) 186, (byte) 228, (byte) 6, (byte) 88, (byte) 25, (byte) 71, (byte) 165, (byte) 251, (byte) 120, (byte) 38, (byte) 196, (byte) 154, (byte) 101, (byte) 59, (byte) 217, (byte) 135, (byte) 4, (byte) 90, (byte) 184, (byte) 230, (byte) 167, (byte) 249, (byte) 27, (byte) 69, (byte) 198, (byte) 152, (byte) 122, (byte) 36, (byte) 248, (byte) 166, (byte) 68, (byte) 26, (byte) 153, (byte) 199, (byte) 37, (byte) 123, (byte) 58, (byte) 100, (byte) 134, (byte) 216, (byte) 91, (byte) 5, (byte) 231, (byte) 185, (byte) 140, (byte) 210, (byte) 48, (byte) 110, (byte) 237,
(byte) 179, (byte) 81, (byte) 15, (byte) 78, (byte) 16, (byte) 242, (byte) 172, (byte) 47, (byte) 113, (byte) 147, (byte) 205, (byte) 17, (byte) 79, (byte) 173, (byte) 243, (byte) 112, (byte) 46, (byte) 204, (byte) 146, (byte) 211, (byte) 141, (byte) 111, (byte) 49, (byte) 178, (byte) 236, (byte) 14, (byte) 80, (byte) 175, (byte) 241, (byte) 19, (byte) 77, (byte) 206, (byte) 144, (byte) 114, (byte) 44, (byte) 109, (byte) 51, (byte) 209, (byte) 143, (byte) 12, (byte) 82, (byte) 176, (byte) 238, (byte) 50, (byte) 108, (byte) 142, (byte) 208, (byte) 83, (byte) 13, (byte) 239, (byte) 177, (byte) 240, (byte) 174, (byte) 76, (byte) 18, (byte) 145, (byte) 207, (byte) 45, (byte) 115, (byte) 202, (byte) 148, (byte) 118, (byte) 40, (byte) 171, (byte) 245, (byte) 23, (byte) 73, (byte) 8,
(byte) 86, (byte) 180, (byte) 234, (byte) 105, (byte) 55, (byte) 213, (byte) 139, (byte) 87, (byte) 9, (byte) 235, (byte) 181, (byte) 54, (byte) 104, (byte) 138, (byte) 212, (byte) 149, (byte) 203, (byte) 41, (byte) 119, (byte) 244, (byte) 170, (byte) 72, (byte) 22, (byte) 233, (byte) 183, (byte) 85, (byte) 11, (byte) 136, (byte) 214, (byte) 52, (byte) 106, (byte) 43, (byte) 117, (byte) 151, (byte) 201, (byte) 74, (byte) 20, (byte) 246, (byte) 168, (byte) 116, (byte) 42, (byte) 200, (byte) 150, (byte) 21, (byte) 75, (byte) 169, (byte) 247, (byte) 182, (byte) 232, (byte) 10, (byte) 84, (byte) 215, (byte) 137, (byte) 107, 53 };
/**
* 计算数组的CRC8校验值
*
* @param data
* 需要计算的数组
* @return CRC8校验值
*/
public static byte calcCrc(byte[] data) {
return calcCrc8(data, 0, data.length, (byte) 0);
}
/**
* 计算CRC8校验值
*
* @param data
* 数据
* @param offset
* 起始位置
* @param len
* 长度
* @return 校验值
*/
public static byte calcCrc8(byte[] data, int offset, int len) {
byte result = data[0];
for (int i = 1; i < len; i++) {
result ^= data[i];
}
return result;
// return calcCrc8(data, offset, len, (byte) 0);
}
/**
* 计算CRC8校验值
*
* @param data
* 数据
* @param offset
* 起始位置
* @param len
* 长度
* @param preval
* 之前的校验值
* @return 校验值
*/
public static byte calcCrc8(byte[] data, int offset, int len, byte preval) {
byte ret = preval;
for (int i = offset; i < (offset + len); ++i) {
ret = crc8_tab[(0x00ff & (ret ^ data[i]))];
}
return ret;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/CRC16Utils.java
================================================
/**
* Copyright (C), 2007-2021, 未来穿戴有限公司
* FileName: CRC16Utils
* Author: Antier
* Date: 2021/7/16 11:46
* Description: 用一句话描述下
*/
package com.luo.bluetooth.protocol;
import android.util.Log;
/**
* @ProjectName: SKG
* @Package: com.king.bluetooth.protocol.neck.util
* @ClassName: CRC16Utils
* @Description: 用一句话描述下
* @Author: Aniter
* @CreateDate: 2021/7/16 11:46
*/
class CRC16Utils {
static byte[] crc16_tab_h = {(byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0,
(byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1,
(byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0,
(byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0,
(byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40};
static byte[] crc16_tab_l = {(byte) 0x00, (byte) 0xC0, (byte) 0xC1, (byte) 0x01, (byte) 0xC3, (byte) 0x03, (byte) 0x02, (byte) 0xC2, (byte) 0xC6, (byte) 0x06, (byte) 0x07, (byte) 0xC7, (byte) 0x05, (byte) 0xC5, (byte) 0xC4, (byte) 0x04, (byte) 0xCC, (byte) 0x0C, (byte) 0x0D, (byte) 0xCD, (byte) 0x0F, (byte) 0xCF, (byte) 0xCE, (byte) 0x0E, (byte) 0x0A, (byte) 0xCA, (byte) 0xCB, (byte) 0x0B, (byte) 0xC9, (byte) 0x09, (byte) 0x08, (byte) 0xC8, (byte) 0xD8, (byte) 0x18, (byte) 0x19, (byte) 0xD9, (byte) 0x1B, (byte) 0xDB, (byte) 0xDA, (byte) 0x1A, (byte) 0x1E, (byte) 0xDE, (byte) 0xDF, (byte) 0x1F, (byte) 0xDD, (byte) 0x1D, (byte) 0x1C, (byte) 0xDC, (byte) 0x14, (byte) 0xD4, (byte) 0xD5, (byte) 0x15, (byte) 0xD7, (byte) 0x17, (byte) 0x16, (byte) 0xD6, (byte) 0xD2, (byte) 0x12,
(byte) 0x13, (byte) 0xD3, (byte) 0x11, (byte) 0xD1, (byte) 0xD0, (byte) 0x10, (byte) 0xF0, (byte) 0x30, (byte) 0x31, (byte) 0xF1, (byte) 0x33, (byte) 0xF3, (byte) 0xF2, (byte) 0x32, (byte) 0x36, (byte) 0xF6, (byte) 0xF7, (byte) 0x37, (byte) 0xF5, (byte) 0x35, (byte) 0x34, (byte) 0xF4, (byte) 0x3C, (byte) 0xFC, (byte) 0xFD, (byte) 0x3D, (byte) 0xFF, (byte) 0x3F, (byte) 0x3E, (byte) 0xFE, (byte) 0xFA, (byte) 0x3A, (byte) 0x3B, (byte) 0xFB, (byte) 0x39, (byte) 0xF9, (byte) 0xF8, (byte) 0x38, (byte) 0x28, (byte) 0xE8, (byte) 0xE9, (byte) 0x29, (byte) 0xEB, (byte) 0x2B, (byte) 0x2A, (byte) 0xEA, (byte) 0xEE, (byte) 0x2E, (byte) 0x2F, (byte) 0xEF, (byte) 0x2D, (byte) 0xED, (byte) 0xEC, (byte) 0x2C, (byte) 0xE4, (byte) 0x24, (byte) 0x25, (byte) 0xE5, (byte) 0x27, (byte) 0xE7,
(byte) 0xE6, (byte) 0x26, (byte) 0x22, (byte) 0xE2, (byte) 0xE3, (byte) 0x23, (byte) 0xE1, (byte) 0x21, (byte) 0x20, (byte) 0xE0, (byte) 0xA0, (byte) 0x60, (byte) 0x61, (byte) 0xA1, (byte) 0x63, (byte) 0xA3, (byte) 0xA2, (byte) 0x62, (byte) 0x66, (byte) 0xA6, (byte) 0xA7, (byte) 0x67, (byte) 0xA5, (byte) 0x65, (byte) 0x64, (byte) 0xA4, (byte) 0x6C, (byte) 0xAC, (byte) 0xAD, (byte) 0x6D, (byte) 0xAF, (byte) 0x6F, (byte) 0x6E, (byte) 0xAE, (byte) 0xAA, (byte) 0x6A, (byte) 0x6B, (byte) 0xAB, (byte) 0x69, (byte) 0xA9, (byte) 0xA8, (byte) 0x68, (byte) 0x78, (byte) 0xB8, (byte) 0xB9, (byte) 0x79, (byte) 0xBB, (byte) 0x7B, (byte) 0x7A, (byte) 0xBA, (byte) 0xBE, (byte) 0x7E, (byte) 0x7F, (byte) 0xBF, (byte) 0x7D, (byte) 0xBD, (byte) 0xBC, (byte) 0x7C, (byte) 0xB4, (byte) 0x74,
(byte) 0x75, (byte) 0xB5, (byte) 0x77, (byte) 0xB7, (byte) 0xB6, (byte) 0x76, (byte) 0x72, (byte) 0xB2, (byte) 0xB3, (byte) 0x73, (byte) 0xB1, (byte) 0x71, (byte) 0x70, (byte) 0xB0, (byte) 0x50, (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52, (byte) 0x92, (byte) 0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94, (byte) 0x54, (byte) 0x9C, (byte) 0x5C, (byte) 0x5D, (byte) 0x9D, (byte) 0x5F, (byte) 0x9F, (byte) 0x9E, (byte) 0x5E, (byte) 0x5A, (byte) 0x9A, (byte) 0x9B, (byte) 0x5B, (byte) 0x99, (byte) 0x59, (byte) 0x58, (byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89, (byte) 0x4B, (byte) 0x8B, (byte) 0x8A, (byte) 0x4A, (byte) 0x4E, (byte) 0x8E, (byte) 0x8F, (byte) 0x4F, (byte) 0x8D, (byte) 0x4D,
(byte) 0x4C, (byte) 0x8C, (byte) 0x44, (byte) 0x84, (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46, (byte) 0x86, (byte) 0x82, (byte) 0x42, (byte) 0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80, (byte) 0x40};
/**
* 计算CRC16校验 对外的接口
*
* @param data 需要计算的数组
* @return CRC16校验值
*/
public static int calcCrc16(byte[] data) {
return calcCrc16(data, 0, data.length);
}
/**
* 计算CRC16校验
*
* @param data 需要计算的数组
* @param offset 起始位置
* @param len 长度
* @return CRC16校验值
*/
public static int calcCrc16(byte[] data, int offset, int len) {
return calcCrc16(data, offset, len, 0xffff);
}
/**
* 计算CRC16校验
*
* @param data 需要计算的数组
* @param offset 起始位置
* @param len 长度
* @param preval 之前的校验值
* @return CRC16校验值
*/
public static int calcCrc16(byte[] data, int offset, int len, int preval) {
int ucCRCHi = (preval & 0xff00) >> 8;
int ucCRCLo = preval & 0x00ff;
int iIndex;
for (int i = 0; i < len; ++i) {
iIndex = (ucCRCLo ^ data[offset + i]) & 0x00ff;
ucCRCLo = ucCRCHi ^ crc16_tab_h[iIndex];
ucCRCHi = crc16_tab_l[iIndex];
}
return ((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff;
}
/**
* 将计算的CRC值 转换为加空格的 比如 : crc值为 A30A -> A3 0A
*
* @param res
* @return
*/
public static String getCrc(int res) {
String format = String.format("%04x", res);
String substring = format.substring(0, 2);
String substring1 = format.substring(2, 4);
Log.i("BLUEDATA", "crc ---- : " + substring + " " + substring1);
return substring.concat(" ").concat(substring1).concat(" ");
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/CrcUtils.java
================================================
/**
* Copyright (C), 2007-2022, 未来穿戴有限公司
* FileName: CrcUtils
* Author: lpq
* Date: 2022/3/11 17:23
* Description: 用一句话描述下
*/
package com.luo.bluetooth.protocol;
/**
*
* @ProjectName: Bluetooth
* @Package: com.luo.bluetooth.protocol
* @ClassName: CrcUtils
* @Description: 用一句话描述下
* @Author: lpq
* @CreateDate: 2022/3/11 17:23
*/
public class CrcUtils {
/**
* crc校验
*/
public static int Crc16Ccitt(int crc, byte[] data) {
for (int i = 0; i < data.length; ++i) {
crc = (crc >> 8) ^ s_tabCrc16CCITT[(crc ^ (data[i])) & 0xff];
}
return crc;
}
/*
* CRC table for the CRC-16-CCITT. The poly is 0x8408 (x^16 + x^12 + x^5 + 1)
*/
static int[] s_tabCrc16CCITT = new int[]{
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/CustomEventListener.java
================================================
package com.luo.bluetooth.protocol;
import com.stag.bluetooth.protocol.OnEventListener;
/**
* 一些由设备端主动发起的蓝牙事件的监听
*/
public interface CustomEventListener extends OnEventListener {
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/CustomPacket.java
================================================
package com.luo.bluetooth.protocol;
import com.stag.bluetooth.packet.Packet;
public class CustomPacket extends Packet {
private byte expandCode; // 扩展码:0x55->读;0x66->写;0xAA->读回应;0x99->写回应
public CustomPacket(int cmd, byte expandCode) {
super(cmd);
this.expandCode = expandCode;
}
public CustomPacket(int cmd, byte[] data, byte expandCode) {
super(cmd, data);
this.expandCode = expandCode;
}
public byte getExpandCode() {
return expandCode;
}
/**
* 用于判断发送的命令是否已经收到回应,没有收到回应底层会重发一次数据
* @param recvPacket 接收到的字节数据处理的结果包
* @return
*/
@Override
public boolean match(Packet recvPacket) {
return super.match(recvPacket);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/CustomProtocol.java
================================================
package com.luo.bluetooth.protocol;
import android.content.Context;
import android.util.Log;
import com.luo.bluetooth.utils.ByteUtils;
import com.stag.bluetooth.protocol.ParseResult;
import com.stag.bluetooth.protocol.Protocol;
import com.stag.bluetooth.protocol.ResultType;
import com.stag.bluetooth.util.LogUtils;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class CustomProtocol extends Protocol {
private static final String TAG = "DscpProtocol";
private final Object recvParseLock = null;
private final byte FRAME_HEADER = 0x7B; // 帧头
private final byte DST_PATH_LEN = 0x01; // 目标路径长度: 多设备拓展长设置,目前只有0x01
private final byte DEVICE_ADDRESS = 0x10; // 地址区域:01->血糖仪;0x20->主机设备
private final byte PHONE_ADDRESS = 0x20; // 地址区域:01->血糖仪;0x20->主机设备
private final byte SRC_PATH_LEN = 0x01; // 源路径长度:多设备拓展长设置,目前只有0x01
private final byte EXPAND_CODE = 0x55; // 扩展码:0x55->读;0x66->写;0xAA->读回应;0x99->写回应
private final byte FRAME_END = 0x7D; // 帧尾
private final int BUFFER_SIZE = 256;
private ByteBuffer recvBuffer;
private CustomPacket recvPacket;
protected CustomProtocol(Context context) {
super(context);
}
public CustomProtocol(Context context, CustomEventListener listener) {
super(context, listener);
}
@Override
public synchronized byte[] packetToBytes(CustomPacket packet) {
/**
* 0x7B:帧头
* 0x01:目标路径长度
* 0x10:目标地址区域
* 0x01:源路径长度
* 0x20:源地址区域
* 0x77:获取SN信息功能码
* 0x55:扩展码 read
* 0x00 0x00:通讯数据长度
* 0x00 ...0x00:通讯数据
* 0x01 0x0B 0x0B 0x04:校验码
* 0x7D:帧尾
*/
//实际打包
int cmd = packet.getCmd();
byte[] cmdData = packet.getData();
byte expandCode = packet.getExpandCode();
ArrayList byteList = new ArrayList();
// 1、加入目标路径长度,目标地址区域,源路径长度,源地址区域
byteList.add(DST_PATH_LEN);
byteList.add(DEVICE_ADDRESS);
byteList.add(SRC_PATH_LEN);
byteList.add(PHONE_ADDRESS);
LogUtils.d(TAG + "lpq", "1、加入基础头部:" + ByteUtils.toString(listToByte(byteList)));
// 2、加入功能码
byteList.add((byte) cmd);
LogUtils.d(TAG + "lpq", "2、加入功能码:" + ByteUtils.toString(listToByte(byteList)));
// 3、加入扩展码
byteList.add(expandCode);
LogUtils.d(TAG + "lpq", "3、加入扩展码:" + ByteUtils.toString(listToByte(byteList)));
// 4、加入通讯数据
if (cmdData != null && cmdData.length != 0) {
byte[] cmdDataLenBytes = ByteUtils.intToBytes2(cmdData.length);
byteList.add(cmdDataLenBytes[0]);
byteList.add(cmdDataLenBytes[1]);
for (byte temp : cmdData) {
byteList.add(temp);
}
} else {
byteList.add((byte) 0x0);
byteList.add((byte) 0x0);
}
Log.i("lpq", "packetToBytes: 4、加入通讯数据:" + ByteUtils.toString(listToByte(byteList)));
// 5、加入校验码
int crcResult = CRC16Utils.calcCrc16(listToByte(byteList));
byte[] crcBytes = ByteUtils.intToBytes2(crcResult);
Log.i("lpq", "packetToBytes: 5、校验码原值:" + ByteUtils.toString(crcBytes));
byteList.add((byte) ((crcBytes[1] >> 4) & 0x0f));
byteList.add((byte) (crcBytes[1] & 0x0f));
byteList.add((byte) ((crcBytes[0] >> 4) & 0x0f));
byteList.add((byte) (crcBytes[0] & 0x0f));
LogUtils.d(TAG + "lpq", "5、加入校验码后:" + ByteUtils.toString(listToByte(byteList)));
// 6、加入头尾帧
byteList.add(0, FRAME_HEADER);
byteList.add(FRAME_END);
LogUtils.d(TAG + "lpq", "6、加入头尾帧:" + ByteUtils.toString(listToByte(byteList)));
return listToByte(byteList);
}
@Override
public ParseResult parse(byte[] data) {
LogUtils.d(TAG + "lpq", "蓝牙接收到数据:data = " + ByteUtils.toString(data));
recvPacket = null;
final ParseResult result = new ParseResult();
if (recvBuffer.position() > 0) {
if (recvBuffer.get(0) != FRAME_HEADER) {
recvBuffer.clear();
}
}
recvBuffer.put(data);
LogUtils.d(TAG + "lpq", "parse: " + ByteUtils.toString(recvBuffer.array()));
LogUtils.d(TAG + "lpq", "parse: position = " + recvBuffer.position());
if (recvBuffer.get(recvBuffer.position() - 1) != FRAME_END) {
result.setType(ResultType.INCOMPLETE);
return result;
}
synchronized (recvParseLock) {
byte[] temp = ByteUtils.subBytes(recvBuffer.array(), 0, recvBuffer.position());
List byteList = byteToList(temp);
//开始解包
// 1、去掉包头包尾
byteList.remove(0);
byteList.remove(byteList.size() - 1);
LogUtils.d(TAG + "lpq", "1、去掉包头包尾后:" + ByteUtils.toString(listToByte(byteList)));
// 2、CRC校验
// 获取校验码
byte[] crcOrigin = new byte[4];
crcOrigin[3] = byteList.remove(byteList.size() - 1);
crcOrigin[2] = byteList.remove(byteList.size() - 1);
crcOrigin[1] = byteList.remove(byteList.size() - 1);
crcOrigin[0] = byteList.remove(byteList.size() - 1);
// 计算校验码
int crcResult = CRC16Utils.calcCrc16(listToByte(byteList));
byte[] crcBytes = ByteUtils.intToBytes2(crcResult);
Log.i("lpq", "packetToBytes: 2、校验码原值:" + ByteUtils.toString(crcOrigin));
byte[] crcCount = new byte[4];
crcCount[0] = ((byte) ((crcBytes[1] >> 4) & 0x0f));
crcCount[1] = ((byte) (crcBytes[1] & 0x0f));
crcCount[2] = ((byte) ((crcBytes[0] >> 4) & 0x0f));
crcCount[3] = ((byte) (crcBytes[0] & 0x0f));
Log.i("lpq", "packetToBytes: 2、校验码计算值:" + ByteUtils.toString(crcCount));
if (crcOrigin[0] == crcCount[0] && crcOrigin[1] == crcCount[1]
&& crcOrigin[2] == crcCount[2] && crcOrigin[3] == crcCount[3]) {
Log.i("lpq", "parse: 校验成功");
} else {
Log.i("lpq", "parse: 校验失败");
}
// 3、去除目标路径长度,目标地址区域,源路径长度,源地址区域
byteList.remove(0);
byteList.remove(0);
byteList.remove(0);
byteList.remove(0);
LogUtils.d(TAG + "lpq", "3、去除基础头部:" + ByteUtils.toString(listToByte(byteList)));
// 4、获取功能码
int cmd = byteList.remove(0);
byte expandCode = byteList.remove(0);
LogUtils.d(TAG + "lpq", "4、获取功能码:" + ByteUtils.toString(listToByte(byteList)));
// 5、获取通讯数据
byte[] lenBytes = new byte[2];
lenBytes[0] = byteList.remove(0);
lenBytes[1] = byteList.remove(0);
int cmdDataLen = ByteUtils.bytesToInt(lenBytes);
byte[] cmdData = listToByte(byteList);
LogUtils.d(TAG + "lpq", "5、获取通讯数据:" + ByteUtils.toString(listToByte(byteList)));
recvPacket = new CustomPacket(cmd, cmdData, expandCode);
LogUtils.d(TAG + "lpq", "parse: " + recvPacket.toString());
}
result.setPacket(recvPacket);
switch (recvPacket.getCmd()) {
// 设备主动上传设备状态
// if (haveSetEventListener()) {
// result.setCallback(new BluetoothDispatch.Callback() {
// @Override
// public void callback() {
// getEventListener().onRecvDeviceStatus(recvPacket.getData());
// }
// });
// }
// result.setType(ACTIVE);
default://理论上都是待响应事件
result.setType(ResultType.RESPOND);
break;
}
recvBuffer.clear();
return result;
}
/**
* list转换成byte
*/
private byte[] listToByte(List byteList) {
if (null == byteList || byteList.size() == 0) {
return null;
}
byte[] bytes = new byte[byteList.size()];
for (int i = 0; i < byteList.size(); ++i) {
bytes[i] = byteList.get(i);
}
return bytes;
}
private List byteToList(byte[] bytes) {
if (bytes == null) {
return null;
}
List byteList = new ArrayList();
for (int i = 0; i < bytes.length; ++i) {
byteList.add(bytes[i]);
}
return byteList;
}
private void addBytes(ArrayList byteList, byte[] encodeBytes) {
for (int i = 0; i < encodeBytes.length; i++) {
byteList.add(encodeBytes[i]);
}
}
/**
* 协议类型,用于支持多协议时的区别
*
* @return
*/
@Override
public int getType() {
return 0;
}
@Override
public UUID getServiceUUID() {
return UUID.fromString("0003CDD0-0000-1000-8000-00805F9B0131");
}
@Override
public UUID getSendTunnelUUID() {
return UUID.fromString("0003CDD2-0000-1000-8000-00805F9B0131");
}
@Override
public UUID getRecvTunnelUUID() {
return UUID.fromString("0003CDD1-0000-1000-8000-00805F9B0131");
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/OnTimeoutResult.java
================================================
/*
* Copyright (c) 2019. stag All rights reserved.
*/
package com.luo.bluetooth.protocol;
/**
* 蓝牙任务回调
*/
public interface OnTimeoutResult {
void onResult(boolean isTimeout, T result);
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/WeiCeCode.java
================================================
/**
* Copyright (C), 2007-2022, 未来穿戴有限公司
* FileName: WeiCeCode
* Author: lpq
* Date: 2022/3/11 16:27
* Description: 用一句话描述下
*/
package com.luo.bluetooth.protocol;
/**
*
* @ProjectName: Bluetooth
* @Package: com.luo.bluetooth.protocol
* @ClassName: WeiCeCode
* @Description: 用一句话描述下
* @Author: lpq
* @CreateDate: 2022/3/11 16:27
*/
public class WeiCeCode {
/**
* ExpandCode
*/
public static final byte EXPAND_CODE_READ = 0x55;
public static final byte EXPAND_CODE_WRITE = 0x66;
public static final byte EXPAND_CODE_READ_RESPONSE = (byte) 0xAA;
public static final byte EXPAND_CODE_WRITE_RESPONSE = (byte) 0x99;
/**
* 获取SN
*/
public static final int GET_SN = 0x77;
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/protocol/WeiCeDeviceOperate.java
================================================
/*
* Copyright (c) 2019. stag All rights reserved.
*/
package com.luo.bluetooth.protocol;
import android.util.Log;
import com.stag.bluetooth.BluetoothTask;
import com.stag.bluetooth.util.ByteUtils;
import java.nio.ByteBuffer;
public class WeiCeDeviceOperate {
private static final String TAG = "DncpDeviceOperate";
public static int ERASE_TIMEOUT = 3000;
public static int ERASE_TRY_COUNT = 2;
public static void getSn(final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
BluetoothTask task = new BluetoothTask(new CustomPacket(WeiCeCode.GET_SN, WeiCeCode.EXPAND_CODE_READ),
callbackInMainThread,
new BluetoothTask.OnDataResultListener() {
@Override
public void onResult(boolean isTimeout, byte[] data) {
Log.i("lpq", "onResult: data = " + ByteUtils.toString(data));
String sn = null;
if (!isTimeout && data != null) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
sn = new String(byteBuffer.array()).replaceAll("[^\u4E00-\u9FA5a-zA-Z0-9-_]+", "");
}
Log.i("lpq", "onResult: sn = " + sn);
obtainResult.onResult(isTimeout, sn);
}
});
task.setTimeout(ERASE_TIMEOUT);
task.send();
}
// public String getTypeBySync() {
// byte[] data = new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_TYPE, address)).sendBySync2();
// String type = null;
// if (data != null) {
// ByteBuffer byteBuffer = ByteBuffer.wrap(data);
// type = new String(byteBuffer.array()).replaceAll("[^\u4E00-\u9FA5a-zA-Z0-9-_]+", "");
// }
// return type;
// }
// public void setType(String type, final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_TYPE,
// ByteUtils.wrapData(type.getBytes(), 16),
// address),
// callbackInMainThread,
// obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Boolean res = Boolean.FALSE;
// if (!isTimeout && data != null) {
// res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).send();
// }
//
//
// public void getModelNumber(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_MODEL, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// String modelNumber = null;
// if (!isTimeout && data != null) {
// ByteBuffer byteBuffer = ByteBuffer.wrap(data);
// modelNumber = new String(byteBuffer.array()).replaceAll("[^\u4E00-\u9FA5a-zA-Z0-9-_]+", "");
// }
// obtainResult.onResult(isTimeout, modelNumber);
// }
// }).send();
// }
//
//
// public void setModelNumber(String model, final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_MODEL,
// ByteUtils.wrapData(model.getBytes(), 16),
// address),
// callbackInMainThread,
// obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Boolean res = Boolean.FALSE;
// if (!isTimeout && data != null) {
// res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).send();
// }
//
//
// public void getManufacturer(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_MANUFACTURER, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// String manufacturer = null;
// if (!isTimeout && data != null) {
// ByteBuffer byteBuffer = ByteBuffer.wrap(data);
// manufacturer = new String(byteBuffer.array()).replace("\0", "");
// //manufacturer = new String(byteBuffer.array()).replaceAll("[^\u4E00-\u9FA5a-zA-Z0-9-_]+", "");
// }
// obtainResult.onResult(isTimeout, manufacturer);
// }
// }).send();
// }
//
//
// public void setManufacturer(String manufacturer, final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_MANUFACTURER,
// ByteUtils.wrapData(manufacturer.getBytes(), 20),
// address),
// callbackInMainThread,
// obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Boolean res = Boolean.FALSE;
// if (!isTimeout && data != null) {
// res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).send();
// }
//
//
// public void getHardWareVersion(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_HARDWARE_VERSION, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
//// Log.e("软件/硬件版本号", "硬件版本号:" + (data == null ? "是个空" : StringUtils.byteArrayToHexString(data)));
//
// Version version = null;
// if (!isTimeout && data != null) {
// version = Version.getVersion(data);
// }
// obtainResult.onResult(isTimeout, version);
// }
// }).send();
// }
//
//
// public void getSoftWareVersion(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SOFT_VERSION, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Version version = null;
// if (!isTimeout && data != null) {
// version = Version.getVersion(data);
// }
// obtainResult.onResult(isTimeout, version);
// }
// }).send();
// }
//
//
// public Version getSoftWareVersionBySync() {
// BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SOFT_VERSION, address));
// task.setTimeout(1500);
// task.setTryCount(2);
// byte[] data = task.sendBySync2();
// Version version = null;
// if (data != null) {
// version = Version.getVersion(data);
// }
// return version;
// }
//
//
// public void getSerialNumber(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SN, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// String serialNumber = null;
// if (!isTimeout && data != null) {
// ByteBuffer byteBuffer = ByteBuffer.wrap(data);
// serialNumber = new String(byteBuffer.array()).replaceAll("[^\u4E00-\u9FA5a-zA-Z0-9-_]+", "");
// }
// obtainResult.onResult(isTimeout, serialNumber);
// }
// }).send();
// }
//
//
// public void getRunMode(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_RUN_MODE, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// DeviceRunMode mode = null;
// if (!isTimeout && data != null) {
// mode = DeviceRunMode.getDeviceRunMode(data[0]);
// }
// obtainResult.onResult(isTimeout, mode);
// }
// }).send();
// }
//
//
// public DeviceRunMode getRunModeBySync() {
// byte[] data = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_RUN_MODE, address))
// .setTimeout(500)
// .sendBySync2();
// DeviceRunMode mode = null;
// if (data != null) {
// mode = DeviceRunMode.getDeviceRunMode(data[0]);
// }
// return mode;
// }
//
//
// public void getVersion(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_VERSION, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Version version = null;
// if (!isTimeout && data != null) {
// version = Version.getVersion(data);
// }
// obtainResult.onResult(isTimeout, version);
// }
// }).send();
// }
//
//
// public void getMaxFragmentSize(@NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_MAX_FRAGMENT_SIZE, address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Short res = null;
// if (!isTimeout && data != null) {
// res = Short.valueOf(ByteUtils.bytesToShort(data));
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).send();
// }
//
//
// public short getMaxFragmentSizeBySync() {
// byte[] data = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_MAX_FRAGMENT_SIZE, address)).sendBySync2();
// short res = 0;
// if (data != null) {
// res = ByteUtils.bytesToShort(data);
// }
// return res;
// }
//
//
// public void enterUpdater() {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ENTER_UPDATER, address)).send();
// }
//
//
// public void enterApp() {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ENTER_APPLICATION, address)).send();
// }
//
//
// public void erase(int address, int size, final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// byte[] data = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address), ByteUtils.intToBytes(size)), 8);
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ERASE, data, this.address),
// callbackInMainThread,
// obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Boolean res = Boolean.FALSE;
// if (!isTimeout && data != null) {
// res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).setTimeout(ERASE_TIMEOUT).setTryCount(ERASE_TRY_COUNT).send();
// }
//
//
// public void writeProgram(int address, int seq, int length, byte[] data,
// final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// byte[] temp = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address),
// ByteUtils.shortToBytes((short) (length & 0xffff)), ByteUtils.shortToBytes((short) (seq & 0xffff)), data), length + 8);
// BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_WRITE_PROGRAM, temp, this.address),
// callbackInMainThread,
// obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// WriteProgramResult res = null;
// if (!isTimeout && data != null) {
// res = WriteProgramResult.getWriteProgramResult(data);
// }
// obtainResult.onResult(isTimeout, res);
// }
// });
// task.setTimeout(2000);
// task.setTryCount(1);
// task.send();
// }
//
// public WriteProgramResult writeProgramBySync(int address, int seq, int length, byte[] data) {
// byte[] temp = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address),
// ByteUtils.shortToBytes((short) (length & 0xffff)), ByteUtils.shortToBytes((short) (seq & 0xffff)), data), length + 8);
// BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_WRITE_PROGRAM, temp, this.address));
// task.setTimeout(2000);
// task.setTryCount(1);
// byte[] d = task.sendBySync2();
// WriteProgramResult res = null;
// if (d != null) {
// res = WriteProgramResult.getWriteProgramResult(d);
// }
// return res;
// }
//
//
// public void checkIntegrity(int checksum, @NonNull final OnTimeoutResult obtainResult, boolean callbackInMainThread) {
// new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_CHECK_INTEGRITY, ByteUtils.intToBytes2SmallDian(checksum), address),
// callbackInMainThread,
// new BluetoothTask.OnDataResultListener() {
// @Override
// public void onResult(boolean isTimeout, byte[] data) {
// Boolean res = Boolean.FALSE;
// if (!isTimeout && data != null) {
// res = ByteUtils.bytes2ToInt2(data) == DSCPCode.DSCP_OK;
// }
// obtainResult.onResult(isTimeout, res);
// }
// }).send();
// }
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/ByteUtils.java
================================================
package com.luo.bluetooth.utils;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import lombok.NonNull;
/**
* Created by Administrator on 2016/7/25 0025.
*/
public class ByteUtils {
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
StringBuilder r = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
r.append(hexArray[(b >> 4) & 0xF]);
r.append(hexArray[(b & 0xF)]);
}
return r.toString();
}
/**
* 缺失部分有效数字
* UUID型转成4字节byte 大端模式,UUID如果不足处于后补零,有效数字在前
*
* @param u
* @return
*/
public static byte[] uuidToByte5BigEndian2(UUID u) {
long l = u.getMostSignificantBits();
byte[] b = new byte[5];
b[4] = (byte) ((l >> 24) & 0xf000000fL);
b[3] = (byte) ((l >> 32) & 0xff00000000L);
b[2] = (byte) ((l >> 40) & 0xff0000000000L);
b[1] = (byte) ((l >> 48) & 0xff000000000000L);
b[0] = (byte) ((l >> 56) & 0xff00000000000000L);
return b;
}
/**
* 4字节byte转化成UUID 大端模式,UUID如果不足处于后补零,有效数字在前
*
* @param b
* @return
*/
public static UUID byte5ToUuidBigEndian2(byte[] b) {
long l = ((long) b[4] << 24) & 0xff000000L;
l |= ((long) b[3] << 32) & 0xff00000000L;
l |= ((long) b[2] << 40) & 0xff0000000000L;
l |= ((long) b[1] << 48) & 0xff000000000000L;
l |= ((long) b[0] << 56) & 0xff00000000000000L;
return new UUID(l, 0);
}
/**
* 缺失部分有效数字
* UUID型转成4字节byte 大端模式
*
* @param u
* @return
*/
public static byte[] uuidToByte5BigEndian(UUID u) {
long l = u.getLeastSignificantBits();
byte[] b = new byte[5];
b[4] = (byte) (l & 0xffL);
b[3] = (byte) ((l >> 8) & 0xff00L);
b[2] = (byte) ((l >> 16) & 0xff0000L);
b[1] = (byte) ((l >> 24) & 0xff000000L);
b[0] = (byte) ((l >> 32) & 0xff00000000L);
return b;
}
/**
* 4字节byte转化成UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte5ToUuidBigEndian(byte[] b) {
long l = b[4] & 0xffL;
l |= (b[3] << 8) & 0xff00L;
l |= (b[2] << 16) & 0xff0000L;
l |= (b[1] << 24) & 0xff000000L;
l |= (b[0] << 32) & 0xff00000000L;
return new UUID(0, l);
}
/**
* 大端模式将3个字节的Byte转成int型
*
* @param b
* @return
*/
public static long byte3ToLongBigEndian(byte[] b) {
long i = (long) b[2] & 0xffL;
i |= (((long) b[1] << 8) & 0xff00L);
i |= (((long) b[1] << 16) & 0xff0000L);
return i;
}
/**
* 大端模式将int型转成3个字节的Byte
*
* @param i
* @return
*/
public static byte[] longToByte3BigEndian(long i) {
byte[] res = new byte[3];
res[2] = (byte) (i & 0xffL);
res[1] = (byte) ((i >> 8) & 0xffL);
res[0] = (byte) ((i >> 16) & 0xffL);
return res;
}
/**
* 比较两个byte数组
*
* @param first
* @param second
* @return
*/
public static boolean compareBytes(byte[] first, byte[] second) {
for (int i = 0; i < (first.length < second.length ? first.length : second.length); i++) {
if (first[i] != second[i]) {
return false;
}
}
return true && first.length == second.length;
}
/**
* 缺失部分有效数字
* UUID型转成4字节byte 大端模式
*
* @param u
* @return
*/
public static byte[] uuidToByte4BigEndian(UUID u) {
long l = u.getLeastSignificantBits();
byte[] b = new byte[4];
b[3] = (byte) (l & 0xffL);
b[2] = (byte) ((l >> 8) & 0xff00L);
b[1] = (byte) ((l >> 16) & 0xff0000L);
b[0] = (byte) ((l >> 24) & 0xff000000L);
return b;
}
/**
* 4字节byte转化成UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte4ToUuidBigEndian(byte[] b) {
long l = (long) b[3] & 0xffL;
l |= ((long) b[2] << 8) & 0xff00L;
l |= ((long) b[1] << 16) & 0xff0000L;
l |= ((long) b[0] << 24) & 0xff000000L;
return new UUID(0, l);
}
/**
* 4字节byte转化成UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte4ToUuidBigEndian2(byte[] b) {
long l = ((long) b[3] << 32) & 0xff00000000L;
l |= ((long) b[2] << 40) & 0xff0000000000L;
l |= ((long) b[1] << 48) & 0xff000000000000L;
l |= ((long) b[0] << 56) & 0xff00000000000000L;
return new UUID(l, 0);
}
/**
* 缺失部分有效数字
* long型转成4字节byte 大端模式
*
* @param l
* @return
*/
public static byte[] longToByte4BigEndian(long l) {
byte[] b = new byte[4];
b[3] = (byte) (l & 0xffL);
b[2] = (byte) ((l >> 8) & 0xff00L);
b[1] = (byte) ((l >> 16) & 0xff0000L);
b[0] = (byte) ((l >> 24) & 0xff000000L);
return b;
}
/**
* 4字节byte转化成Long 大端模式
*
* @param b
* @return
*/
public static long byte4ToLongBigEndian(byte[] b) {
long l = (long) b[3] & 0xffL;
l |= ((long) b[2] << 8) & 0xff00L;
l |= ((long) b[1] << 16) & 0xff0000L;
l |= ((long) b[0] << 24) & 0xff000000L;
return l;
}
/**
* 16个字节转化UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte16ToUuidBigEndian(byte[] b) {
return new UUID(byte8ToLongBigEndian(subBytes(b, 0, 8)), byte8ToLongBigEndian(subBytes(b, 8, 8)));
}
/**
* UUID转化成16个字节 大端模式
*
* @param uuid
* @return
*/
public static byte[] uuidToByte16BigEndian(UUID uuid) {
long l = uuid.getLeastSignificantBits();
long l2 = uuid.getMostSignificantBits();
return combineByteArray(longToByte8BigEndian(l2), longToByte8BigEndian(l));
}
/**
* 8个字节转化UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte8ToUuidBigEndian(byte[] b) {
return new UUID(0, byte8ToLongBigEndian(b));
}
/**
* UUID转化成8个字节 大端模式
*
* @param uuid
* @return
*/
public static byte[] uuidToByte8BigEndian(UUID uuid) {
long l = uuid.getLeastSignificantBits();
return longToByte8BigEndian(l);
}
/**
* 8个字节转化成long型 大端模式
*
* @param b
* @return
*/
public static long byte8ToLongBigEndian(byte[] b) {
long l = (long) b[7] & 0xffL;
l |= ((long) b[6] << 8) & 0xff00L;
l |= ((long) b[5] << 16) & 0xff0000L;
l |= ((long) b[4] << 24) & 0xff000000L;
l |= ((long) b[3] << 32) & 0xff00000000L;
l |= ((long) b[2] << 40) & 0xff0000000000L;
l |= ((long) b[1] << 48) & 0xff000000000000L;
l |= ((long) b[0] << 56) & 0xff00000000000000L;
return l;
}
/**
* long型转成8个字节 大端模式
*
* @param l
* @return
*/
public static byte[] longToByte8BigEndian(long l) {
byte[] b = new byte[8];
b[7] = (byte) (l & 0xffL);
b[6] = (byte) ((l >> 8) & 0xffL);
b[5] = (byte) ((l >> 16) & 0xffL);
b[4] = (byte) ((l >> 24) & 0xffL);
b[3] = (byte) ((l >> 32) & 0xffL);
b[2] = (byte) ((l >> 40) & 0xffL);
b[1] = (byte) ((l >> 48) & 0xffL);
b[0] = (byte) ((l >> 56) & 0xffL);
return b;
}
/**
* 六个字节的byte转化成UUID,有效数字在前,于后补零
*
* @param b
* @return
*/
public static UUID byte6ToUuidBigEndian2(byte[] b) {
long l = ((long) b[5] << 16) & 0xff0000L;
l |= ((long) b[4] << 24) & 0xff000000L;
l |= ((long) b[3] << 32) & 0xff00000000L;
l |= ((long) b[2] << 40) & 0xff0000000000L;
l |= ((long) b[1] << 48) & 0xff000000000000L;
l |= ((long) b[0] << 56) & 0xff00000000000000L;
return new UUID(l, 0);
}
/**
* 将UUID转成六个字节的Byte,有效数字在前,于后补零
*
* @param uuid
* @return
*/
public static byte[] uuidToByte6BigEndian2(UUID uuid) {
long l = uuid.getMostSignificantBits();
byte[] b = new byte[6];
b[5] = (byte) ((l >> 16) & 0xffL);
b[4] = (byte) ((l >> 24) & 0xffL);
b[3] = (byte) ((l >> 32) & 0xffL);
b[2] = (byte) ((l >> 40) & 0xffL);
b[1] = (byte) ((l >> 48) & 0xffL);
b[0] = (byte) ((l >> 56) & 0xffL);
return b;
}
/**
* 六个字节的byte转化成UUID
*
* @param b
* @return
*/
public static UUID byte6ToUuidBigEndian(byte[] b) {
long l = (long) b[5] & 0xffL;
l |= ((long) b[4] << 8) & 0xff00L;
l |= ((long) b[3] << 16) & 0xff0000L;
l |= ((long) b[2] << 24) & 0xff000000L;
l |= ((long) b[1] << 32) & 0xff00000000L;
l |= ((long) b[0] << 40) & 0xff0000000000L;
return new UUID(0, l);
}
/**
* 将UUID转成六个字节的Byte
*
* @param uuid
* @return
*/
public static byte[] uuidToByte6BigEndian(UUID uuid) {
long l = uuid.getLeastSignificantBits();
byte[] b = new byte[6];
b[5] = (byte) (l & 0xffL);
b[4] = (byte) ((l >> 8) & 0xffL);
b[3] = (byte) ((l >> 16) & 0xffL);
b[2] = (byte) ((l >> 24) & 0xffL);
b[1] = (byte) ((l >> 32) & 0xffL);
b[0] = (byte) ((l >> 40) & 0xffL);
return b;
}
/**
* 大端模式将4个字节的Byte转成int型
*
* @param b
* @return
*/
public static int byte4ToIntBigEndian(byte[] b) {
int i = b[3] & 0xff;
i |= ((b[2] << 8) & 0xff00);
i |= ((b[1] << 16) & 0xff0000);
i |= ((b[0] << 24) & 0xff000000);
return i;
}
/**
* 大端模式将int型转成4个字节的Byte
*
* @param i
* @return
*/
public static byte[] intToByte4BigEndian(int i) {
byte[] res = new byte[4];
res[3] = (byte) (i & 0xff);
res[2] = (byte) ((i >> 8) & 0xff);
res[1] = (byte) ((i >> 16) & 0xff);
res[0] = (byte) ((i >> 24) & 0xff);
return res;
}
/**
* 大端模式将3个字节的Byte转成int型
*
* @param b
* @return
*/
public static int byte3ToIntBigEndian(byte[] b) {
int i = b[2] & 0xff;
i |= ((b[1] << 8) & 0xff00);
i |= ((b[1] << 16) & 0xff0000);
return i;
}
/**
* 大端模式将int型转成3个字节的Byte
*
* @param i
* @return
*/
public static byte[] intToByte3BigEndian(int i) {
byte[] res = new byte[3];
res[2] = (byte) (i & 0xff);
res[1] = (byte) ((i >> 8) & 0xff);
res[0] = (byte) ((i >> 16) & 0xff);
return res;
}
public static String toString(byte[] data) {
return toString(data, " ");
}
public static String toString(byte[] data, String interval) {
if (data == null) {
return null;
}
String iStr = interval == null ? "" : interval;
StringBuilder buffer = new StringBuilder();
for (byte b : data) {
String d = Integer.toHexString(b & 0xff);
if (d.length() > 1) {
buffer.append(d).append(iStr);
} else {
buffer.append("0").append(d).append(iStr);
}
}
return buffer.toString();
}
public static byte[] subBytes(byte[] data, int start, int len) {
byte[] res = new byte[len];
for (int i = 0; i < len; i++) {
res[i] = data[start + i];
}
return res;
}
public static byte[] subBytes(byte[] data, int start) {
int len = data.length - start;
return subBytes(data, start, len);
}
/**
* 将字节数组转化成Long
*
* @param b
* @return
*/
public static long bytesToLong(byte[] b) {
long l = ((long) b[0] << 56) & 0xFF00000000000000L;
// 如果不强制转换为long,那么默认会当作int,导致最高32位丢失
l |= ((long) b[1] << 48) & 0xFF000000000000L;
l |= ((long) b[2] << 40) & 0xFF0000000000L;
l |= ((long) b[3] << 32) & 0xFF00000000L;
l |= ((long) b[4] << 24) & 0xFF000000L;
l |= ((long) b[5] << 16) & 0xFF0000L;
l |= ((long) b[6] << 8) & 0xFF00L;
l |= b[7] & 0xFFL;
return l;
}
/**
* 将Long转化成字节数组
*
* @param l
* @return
*/
public static byte[] longToBytes(long l) {
byte[] b = new byte[8];
b[0] = (byte) (l >>> 56);
b[1] = (byte) (l >>> 48);
b[2] = (byte) (l >>> 40);
b[3] = (byte) (l >>> 32);
b[4] = (byte) (l >>> 24);
b[5] = (byte) (l >>> 16);
b[6] = (byte) (l >>> 8);
b[7] = (byte) (l);
return b;
}
/**
* 合并Byte数组
*
* @param bytes
* @return
*/
public static byte[] combineByteArray(byte[]... bytes) {
int len = 0;
for (byte[] bs : bytes) {
if (bs != null) {
len += bs.length;
}
}
byte[] res = new byte[len];
int pos = 0;
for (byte[] bs : bytes) {
if (bs != null) {
for (byte b : bs) {
res[pos++] = b;
}
}
}
return res;
}
/**
* 将UUID转化成Byte数组
*
* @param uuid
* @return
*/
public static byte[] uuidToBytes(UUID uuid) {
return combineByteArray(longToBytes(uuid.getMostSignificantBits()), longToBytes(uuid.getLeastSignificantBits()));
}
/**
* 将Byte数组转化成UUID
*
* @param data
* @return
*/
public static UUID bytesToUuid(byte[] data) {
if (data == null || data.length < 16)
return null;
byte[] bMost = subBytes(data, 0, 8);
byte[] bLeast = subBytes(data, 8, 8);
return new UUID(bytesToLong(bMost), bytesToLong(bLeast));
}
/**
* 将Int转化为Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytes2(int i) {
byte[] res = new byte[2];
res[1] = (byte) (i & 0xFF);
res[0] = (byte) (i >> 8 & 0xFF);
return res;
}
public static byte[] intToBytes2SmallDian(int i) {
byte[] res = new byte[2];
res[0] = (byte) (i & 0xFF);
res[1] = (byte) (i >> 8 & 0xFF);
return res;
}
/**
* 将Byte数组转化为int,取第一二个字节
*
* @param b
* @return
*/
public static int bytes2ToInt(byte[] b) {
int c = (int) ((b[0] << 8) & 0xFF00L);
c |= (int) (b[1] & 0xFFL);
return c;
}
/**
* 将Byte数组转化为int,取第一二个字节,小端模式
*
* @param b
* @return
*/
public static int bytesS2ToInt(byte[] b) {
int c = (int) ((b[1] << 8) & 0xFF00L);
c |= (int) (b[0] & 0xFFL);
return c;
}
/**
* 将Byte数组转化为int,取第一三个字节
*
* @param b
* @return
*/
public static int bytes2ToInt2(byte[] b) {
int c = (int) ((b[1] << 16) & 0xFFFFFF00L);
c |= (int) (b[0]);
return c;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToIntBigEndian(byte[] b) {
int res = (int) ((b[0] << 24) & 0xFF000000L);
res |= (b[1] << 16 & 0xFF0000L);
res |= (b[2] << 8 & 0xFF00L);
res |= (b[3] & 0xFFL);
return res;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToIntSmallEndian(byte[] b) {
int res = (int) ((b[3] << 24) & 0xFF000000L);
res |= (b[2] << 16 & 0xFF0000L);
res |= (b[1] << 8 & 0xFF00L);
res |= (b[0] & 0xFFL);
return res;
}
/**
* 将Int转化成Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytesBigEndian(int i) {
byte[] res = new byte[4];
res[0] = (byte) ((i >> 24) & 0xFFL);
res[1] = (byte) ((i >> 16) & 0xFFL);
res[2] = (byte) ((i >> 8) & 0xFFL);
res[3] = (byte) (i & 0xFFL);
return res;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToInt(byte[] b) {
int res = (int) ((b[3] << 24) & 0xFF000000L);
res |= (b[2] << 16 & 0xFF0000L);
res |= (b[1] << 8 & 0xFF00L);
res |= (b[0] & 0xFFL);
return res;
}
/**
* 将Int转化成Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytes(int i) {
byte[] res = new byte[4];
res[3] = (byte) ((i >> 24) & 0xFFL);
res[2] = (byte) ((i >> 16) & 0xFFL);
res[1] = (byte) ((i >> 8) & 0xFFL);
res[0] = (byte) (i & 0xFFL);
return res;
}
/**
* 将Byte数组转化成short
*
* @param b
* @return
*/
public static short bytesToShort(byte[] b) {
short res = (short) ((b[1] << 8) & 0xFF00l);
res |= (b[0] & 0xFFl);
return res;
}
/**
* 将Short转化为Byte数组
*
* @param i
* @return
*/
public static byte[] shortToBytes(short i) {
byte[] res = new byte[2];
res[1] = (byte) ((i >> 8) & 0xFFL);
res[0] = (byte) (i & 0xFFL);
return res;
}
/**
* 大端序
* 将Byte数组转化成short
*
* @param b
* @return
*/
public static short bytesToShortBigEndian(byte[] b) {
short res = (short) ((b[0] << 8) & 0xFF00L);
res |= (b[1] & 0xFFL);
return res;
}
/**
* 大端序
* 将Short转化为Byte数组
*
* @param i
* @return
*/
public static byte[] shortToBytesBigEndian(short i) {
byte[] res = new byte[2];
res[0] = (byte) ((i >> 8) & 0xFFL);
res[1] = (byte) (i & 0xFFL);
return res;
}
/**
* 填充数据至长度为N的数组
*
* @param data
* @param len
* @return
*/
public static byte[] wrapData(byte[] data, int len) {
byte[] res = new byte[len];
for (int i = 0; i < len; i++) {
if (i < data.length) {
res[i] = data[i];
} else {
res[i] = 0;
}
}
return res;
}
/**
* 取出一字节中每一位的数值
*
* @param data
* @return
*/
public static byte[] getByteBits(byte data) {
byte[] result = new byte[8];
for (int i = 0; i < 8; i++) {
result[i] = (byte) (data >> i & 0x01);
}
return result;
}
public static byte[] getBytesWithBuffer(ByteBuffer buffer) {
if (buffer == null)
return null;
byte[] bytes = new byte[buffer.position()];
for (int i = 0; i < buffer.position(); i++)
bytes[i] = buffer.array()[i];
return bytes;
}
public static boolean equal(byte[] data1, byte[] data2) {
return compare(data1, data2) == 0;
}
public static int compare(byte[] bytes1, byte[] bytes2) {
if (bytes1.length > bytes2.length) {
return 1;
}
if (bytes1.length < bytes2.length) {
return -1;
}
for (int i = 0; i < bytes1.length; i++) {
int result = (bytes1[i] & 0xFF) - (bytes2[i] & 0xFF);
if (result > 0) {
return 1;
}
if (result < 0) {
return -1;
}
}
return 0;
}
public static byte getXor(byte[] data) {
return getXor(data, 0, data.length);
}
public static byte getXor(byte[] data, int start, int len) {
byte xor = 0;
for (int i = start; i < len + start; i++)
xor ^= data[i];
return xor;
}
/**
* bytes 转Ascii码字符串
*/
public static String bytesToAscii(byte[] bytes, int offset, int dateLen) {
if ((bytes == null) || (bytes.length == 0) || (offset < 0) || (dateLen <= 0)) {
return null;
}
if ((offset >= bytes.length) || (bytes.length - offset < dateLen)) {
return null;
}
String asciiStr = null;
byte[] data = new byte[dateLen];
System.arraycopy(bytes, offset, data, 0, dateLen);
asciiStr = new String(data, StandardCharsets.US_ASCII);
return asciiStr;
}
public static String bytesToAscii(byte[] bytes, int dateLen) {
return bytesToAscii(bytes, 0, dateLen);
}
public static String bytesToAscii(byte[] bytes) {
return bytesToAscii(bytes, 0, bytes.length);
}
public static byte[] asciiToBytes(String string) {
if (string == null) {
return null;
}
return string.getBytes(StandardCharsets.US_ASCII);
}
/**
* 16 进制字符串转bytes数据
*/
public static byte[] hexStringToBytes(@NonNull String hexString) {
byte[] result = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length() / 2; ++i) {
String temp = hexString.substring(2 * i, 2 * (i + 1));
result[i] = (byte) Integer.parseInt(temp, 16);
}
return result;
}
public static String addSpaceToString(String origin, int strLength) {
if (origin == null) {
origin = "";
}
String result = origin;
if (origin.length() < strLength) {
StringBuilder sb = new StringBuilder();
sb.append(origin);// 左补0
while (sb.length() < strLength) {
sb.append(" ");
}
result = sb.toString();
}
return result;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/DateUtils.java
================================================
package com.luo.bluetooth.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 日期工具类
*
* @author jingle1267@163.com
*/
public final class DateUtils {
/**
* 日期类型 *
*/
public static final String yyyyMMDD = "yyyy-MM-dd";
public static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss";
public static final String HH_mm_ss = "HH:mm:ss";
public static final String HHmmss = "HHmmss";
public static final String yyMMddHHmm = "yyMMddHHmm";
public static final String ddHHmmss = "ddHHmmss";
public static final String hhmmss = "HH:mm:ss";
public static final String HH_mm_ss_SSS = "HH:mm:ss.SSS";
public static final String LOCALE_DATE_FORMAT = "yyyy年M月d日 HH:mm:ss";
public static final String DB_DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String NEWS_ITEM_DATE_FORMAT = "hh:mm M月d日 yyyy";
public static final String COMMON_TYPE = "yyyyMMddHHmmss";
public static final String MMddHHmmss = "MM月dd日HH时mm分ss秒";
public static final long SECOND = 1000;//1000毫秒
public static final long MINUTE = 60 * SECOND;
public static final long HOUR = 60 * MINUTE;
public static final long DAY = 24 * HOUR;
public static String getCurrentDateString() {
return formatDate(new Date(), yyyyMMddHHmmss);
}
/**
* 在原基础上加N个小时
*
* @param date
* @param hour
* @return
*/
public static Date dateAddHours(Date date, int hour) {
long timestamp = date.getTime() + hour * 60 * 60 * 1000;
return new Date(timestamp);
}
public static String dateToString(long time, String pattern)
throws Exception {
return dateToString(new Date(time), pattern);
}
public static String dateToString(Date date, String pattern)
throws Exception {
return new SimpleDateFormat(pattern).format(date);
}
public static Date stringToDate(String dateStr, String pattern)
throws Exception {
return new SimpleDateFormat(pattern).parse(dateStr);
}
public static String formatDate(long time, String type) {
return formatDate(new Date(time), type);
}
public static String formatDate(Date date) {
return formatDate(date, yyyyMMddHHmmss);
}
/**
* 将Date类型转换为日期字符串
*
* @param date Date对象
* @param type 需要的日期格式
* @return 按照需求格式的日期字符串
*/
public static String formatDate(Date date, String type) {
try {
SimpleDateFormat df = new SimpleDateFormat(type);
return df.format(date);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将日期字符串转换为Date类型
*
* @param dateStr 日期字符串
* @param type 日期字符串格式
* @return Date对象
*/
public static Date parseDate(String dateStr, String type) {
SimpleDateFormat df = new SimpleDateFormat(type);
Date date = null;
try {
date = df.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
/**
* 得到年
*
* @param date Date对象
* @return 年
*/
public static int getYear(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.YEAR);
}
/**
* 得到月
*
* @param date Date对象
* @return 月
*/
public static int getMonth(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.MONTH) + 1;
}
/**
* 得到日
*
* @param date Date对象
* @return 日
*/
public static int getDay(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.DAY_OF_MONTH);
}
public static int getHour(Date date){
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.HOUR_OF_DAY);
}
public static int getMinute(Date date){
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.MINUTE);
}
public static int getSecond(Date date){
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.SECOND);
}
/**
* 转换日期 将日期转为今天, 昨天, 前天, XXXX-XX-XX, ...
*
* @param time 时间
* @return 当前日期转换为更容易理解的方式
*/
public static String translateDate(Long time) {
long oneDay = 24 * 60 * 60 * 1000;
Calendar current = Calendar.getInstance();
Calendar today = Calendar.getInstance(); //今天
today.set(Calendar.YEAR, current.get(Calendar.YEAR));
today.set(Calendar.MONTH, current.get(Calendar.MONTH));
today.set(Calendar.DAY_OF_MONTH, current.get(Calendar.DAY_OF_MONTH));
// Calendar.HOUR——12小时制的小时数 Calendar.HOUR_OF_DAY——24小时制的小时数
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
long todayStartTime = today.getTimeInMillis();
if (time >= todayStartTime && time < todayStartTime + oneDay) { // today
return "今天";
} else if (time >= todayStartTime - oneDay && time < todayStartTime) { // yesterday
return "昨天";
} else if (time >= todayStartTime - oneDay * 2 && time < todayStartTime - oneDay) { // the day before yesterday
return "前天";
} else if (time > todayStartTime + oneDay) { // future
return "将来某一天";
} else {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date(time);
return dateFormat.format(date);
}
}
/**
* 转换日期 转换为更为人性化的时间
*
* @param time 时间
* @return
*/
private String translateDate(long time, long curTime) {
long oneDay = 24 * 60 * 60;
Calendar today = Calendar.getInstance(); //今天
today.setTimeInMillis(curTime * 1000);
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
long todayStartTime = today.getTimeInMillis() / 1000;
if (time >= todayStartTime) {
long d = curTime - time;
if (d <= 60) {
return "1分钟前";
} else if (d <= 60 * 60) {
long m = d / 60;
if (m <= 0) {
m = 1;
}
return m + "分钟前";
} else {
SimpleDateFormat dateFormat = new SimpleDateFormat("今天 HH:mm");
Date date = new Date(time * 1000);
String dateStr = dateFormat.format(date);
if (!TextUtils.isEmpty(dateStr) && dateStr.contains(" 0")) {
dateStr = dateStr.replace(" 0", " ");
}
return dateStr;
}
} else {
if (time < todayStartTime && time > todayStartTime - oneDay) {
SimpleDateFormat dateFormat = new SimpleDateFormat("昨天 HH:mm");
Date date = new Date(time * 1000);
String dateStr = dateFormat.format(date);
if (!TextUtils.isEmpty(dateStr) && dateStr.contains(" 0")) {
dateStr = dateStr.replace(" 0", " ");
}
return dateStr;
} else if (time < todayStartTime - oneDay && time > todayStartTime - 2 * oneDay) {
SimpleDateFormat dateFormat = new SimpleDateFormat("前天 HH:mm");
Date date = new Date(time * 1000);
String dateStr = dateFormat.format(date);
if (!TextUtils.isEmpty(dateStr) && dateStr.contains(" 0")) {
dateStr = dateStr.replace(" 0", " ");
}
return dateStr;
} else {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = new Date(time * 1000);
String dateStr = dateFormat.format(date);
if (!TextUtils.isEmpty(dateStr) && dateStr.contains(" 0")) {
dateStr = dateStr.replace(" 0", " ");
}
return dateStr;
}
}
}
/**
* 计算剩余时间
*
* @param time
* @param curTime
* @return
*/
public static String leftTime(long time, long curTime) {
StringBuffer buf = new StringBuffer();
long leftTime = time - curTime;
if (leftTime < 0) {
buf.append("Expired");
} else if (leftTime >= 0 && leftTime < 1000) {
buf.append("Expired");
} else {
if (leftTime / DAY > 0) {
buf.append((leftTime / DAY) + "Day(s)");
leftTime %= DAY;
}
if (leftTime / HOUR > 0) {
buf.append((leftTime / HOUR) + "Hour(s)");
leftTime %= HOUR;
}
if (leftTime / MINUTE > 0) {
buf.append((leftTime / MINUTE) + "Minute(s)");
leftTime %= MINUTE;
}
if (leftTime / SECOND > 0) {
buf.append((leftTime / SECOND) + "Second(s)");
leftTime %= SECOND;
}
}
return buf.toString();
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/DialogUtils.java
================================================
package com.luo.bluetooth.utils;
import android.app.Dialog;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.os.Handler;
import android.view.View;
import com.luo.bluetooth.R;
import com.luo.bluetooth.customview.searchble.Effectstype;
import com.luo.bluetooth.customview.searchble.NiftyDialogBuilder;
import cn.pedant.SweetAlert.SweetAlertDialog;
/**
* Created by Bruce on 2017/4/12 0012.
*/
public class DialogUtils {
private static final short DIALOG_SHOW_TIME_SHORT = 1500;
private static final short DIALOG_SHOW_TIME_LONG = 2500;
private static NiftyDialogBuilder dialogBuilder;
private static SweetAlertDialog dialog;
public static void showProgressbarDialog(Context context) {
showProgressbarDialog(context, null);
}
public static void showProgressbarDialog(Context context, int titleId) {
showProgressbarDialog(context, context.getString(titleId));
}
public static void showProgressbarDialog(Context context, String title) {
if (!checkReuseable(context, SweetAlertDialog.PROGRESS_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.PROGRESS_TYPE);
}
dialog.setTitleText(title);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
public static void showNormalDialog(Context context, int text) {
showNormalDialog(context, context.getString(text));
}
public static void showNormalDialog(Context context, String text) {
showNormalDialog(context, text, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showNormalDialog(Context context, String text, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);
}
dialog.setContentText(text)
.setTitleText(null)
.setConfirmClickListener(listener)
.show();
}
public static SweetAlertDialog showNormalDialog(Context context, String text,
SweetAlertDialog.OnSweetClickListener listener,
SweetAlertDialog.OnSweetClickListener cancelListener) {
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);
}
dialog.setContentText(text)
.setTitleText(null)
.setConfirmClickListener(listener)
.setCancelClickListener(cancelListener)
.show();
return dialog;
}
public static void showNormalDialog(Context context, String title, String text, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);
}
dialog.setContentText(text)
.setTitleText(title)
.setConfirmClickListener(listener)
.show();
}
public static SweetAlertDialog showNormalDialog(Context context, String title, String text,
String confirmText,
SweetAlertDialog.OnSweetClickListener listener,
String cancelText,
SweetAlertDialog.OnSweetClickListener cancelListener) {
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);
}
dialog.setContentText(text)
.setTitleText(title)
.setConfirmText(confirmText)
.setConfirmClickListener(listener)
.setCancelText(cancelText)
.setCancelClickListener(cancelListener)
.show();
return dialog;
}
public static SweetAlertDialog showNormalDialog(Context context, String title,
String confirmText,
SweetAlertDialog.OnSweetClickListener listener,
String cancelText,
SweetAlertDialog.OnSweetClickListener cancelListener) {
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);
}
dialog.setTitleText(title)
.setConfirmText(confirmText)
.setConfirmClickListener(listener)
.setCancelText(cancelText)
.setCancelClickListener(cancelListener)
.show();
return dialog;
}
public static void showLongErrorDialog(final Context context, String title) {
showLongErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showLongErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
showErrorDialog(context, title, DIALOG_SHOW_TIME_LONG, listener);
}
public static void showShortErrorDialog(final Context context, int title) {
showShortErrorDialog(context, context.getString(title));
}
public static void showShortErrorDialog(final Context context, String title) {
showShortErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showShortErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
showErrorDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);
}
public static void showErrorDialog(Context context, int title) {
showErrorDialog(context, context.getString(title));
}
public static void showErrorDialog(Context context, String title) {
showErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showErrorDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {
showErrorDialog(context, title, listener);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dismissDialog(true);
}
}, duration);
}
public static void showErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.ERROR_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.ERROR_TYPE);
}
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setTitleText(title).setConfirmClickListener(listener);
dialog.show();
}
public static void showErrorDialog(Context context, String title,String content, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.ERROR_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.ERROR_TYPE);
}
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setTitleText(title).setContentText(content).setConfirmClickListener(listener);
dialog.show();
}
public static void showShortSuccessDialog(Context context, int title) {
showShortSuccessDialog(context, context.getString(title));
}
public static void showShortSuccessDialog(Context context, String title) {
showShortSuccessDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showShortSuccessDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
showSuccessDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);
}
public static void showSuccessDialog(Context context, int title) {
showSuccessDialog(context, context.getString(title));
}
public static void showSuccessDialog(Context context, String title) {
showSuccessDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showSuccessDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {
showSuccessDialog(context, title, listener);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dismissDialog(true);
}
}, duration);
}
public static void showSuccessDialog(Context context, int title, SweetAlertDialog.OnSweetClickListener listener) {
showSuccessDialog(context, context.getString(title), listener);
}
public static void showSuccessDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.SUCCESS_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.SUCCESS_TYPE);
}
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setTitleText(title)
.setConfirmClickListener(listener)
.show();
}
public static void showSuccessDialog(Context context, String title, String content, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.SUCCESS_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.SUCCESS_TYPE);
}
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setTitleText(title)
.setContentText(content)
.setConfirmClickListener(listener)
.show();
}
public static void showShortWarningDialog(Context context, int title) {
showShortWarningDialog(context, context.getString(title));
}
public static void showShortWarningDialog(Context context, String title) {
showShortWarningDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showShortWarningDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
showWarningDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);
}
public static void showWarningDialog(Context context, int id) {
showWarningDialog(context, context.getString(id));
}
public static void showWarningDialog(Context context, String title) {
showWarningDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
dismissDialog(true);
}
});
}
public static void showWarningDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {
showWarningDialog(context, title, listener);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dismissDialog(true);
}
}, duration);
}
public static void showWarningDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);
}
dialog.setTitleText(title).setConfirmClickListener(listener);
dialog.show();
}
public static void showWarningDialog(Context context, String title,String content, SweetAlertDialog.OnSweetClickListener listener) {
if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);
}
dialog.setTitleText(title).setContentText(content).setConfirmClickListener(listener);
dialog.show();
}
public static void showWarningDialog(Context context, String title,String content, String confirmText,SweetAlertDialog.OnSweetClickListener confirmListener,
String cancelText, SweetAlertDialog.OnSweetClickListener cancelListener) {
if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){
dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);
}
dialog.setTitleText(title)
.setContentText(content)
.setConfirmText(confirmText)
.setConfirmClickListener(confirmListener)
.setCancelText(cancelText)
.setCancelClickListener(cancelListener);
dialog.show();
}
public static SweetAlertDialog showCancelableDialog(Context context, String text, SweetAlertDialog.OnSweetClickListener listener){
if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){
dialog = new SweetAlertDialog(context,SweetAlertDialog.NORMAL_TYPE);
}
dialog.setCancelText("取消")
.setConfirmText("确认")
.setContentText(text)
.setTitleText(null)
.setCancelClickListener(listener);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.show();
return dialog;
}
private static boolean checkReuseable(Context context, int dialogType){
if (dialog==null)
return false;
if (!isMatchingCurrentContext(context) || dialog.getAlerType() != dialogType){
dialog.dismiss();
dialog = null;
return false;
}
return true;
}
public static boolean isMatchingCurrentContext(Context context){
return ((ContextWrapper)dialog.getContext()).getBaseContext() == context;
}
public static Dialog getDialog(){
return dialog;
}
public static void dismissDialog() {
dismissDialog(false);
}
public static void dismissDialog(boolean animate) {
if (dialog!=null){
if (animate)
dialog.cancel();
else
dialog.dismiss();
dialog = null;
}
}
public static void dismissMatchCurrentDialog(Context context){
if (dialog!=null && isMatchingCurrentContext(context))
dismissDialog(false);
}
/******************************************NiftyDialogBuilder************************************************/
/**
* 弹出提示框
*
* @param titleId
* @param contentId
*/
public static void showDialog(Context context, int titleId, int contentId) {
String title = context.getString(titleId);
String content = context.getString(contentId);
showNiftyTipDialog(context, title, content);
}
public static void showNiftyTipDialog(Context context, String title, String content) {
if (dialogBuilder == null) {
dialogBuilder = NiftyDialogBuilder.getInstance(context);
}
dialogBuilder.withTitle(title)//.withTitle(null) no title
.removeAllCustomView()
.withEffect(Effectstype.values()[9])
.withTitleColor("#FFFFFF") //def
.withDividerColor("#11000000") //def
.withMessage(content) //.withMessage(null) no Msg
.withMessageColor("#FFFFFFFF") //def | withMessageColor(int resid)
.withDialogColor("#33ffff") //def | withDialogColor(int resid)
// .withIcon(ContextCompat.getDrawable(context, R.mipmap.icon_staginfo))
.withDuration(700) //def
// .withEffect(effect) //def Effectstype.Slidetop
.withButton1Text(context.getString(R.string.btn_ok)) //def gone
.isCancelableOnTouchOutside(true) //def | isCancelable(true)
// .setCustomView(R.layout.custom_view,v.getContext()) //.setCustomView(View or ResId,context)
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialogBuilder.dismiss();
}
})
.show();
}
public static void showNiftyCustonDialog(Context context, int title, View view) {
showNiftyCustonDialog(context, context.getString(title), view);
}
public static void showNiftyCustonDialog(Context context, String title, View view) {
showNiftyCustonDialog(context, title, view, null);
}
public static void showNiftyCustonDialog(Context context, String title, View view, DialogInterface.OnDismissListener listener) {
dialogBuilder = NiftyDialogBuilder.getInstance(context);
dialogBuilder.withTitle(title)//.withTitle(null) no title
.setCustomView(view, context)
.withEffect(Effectstype.values()[9])
.withTitleColor("#FFFFFF") //def
.withDividerColor("#FFFFFF") //def
.withMessage(null) //.withMessage(null) no Msg
.withMessageColor("#FFFFFF") //def | withMessageColor(int resid)
.withDialogColor("#1fbaf3") //def | withDialogColor(int resid)
// .withIcon(ContextCompat.getDrawable(context, R.mipmap.icon_staginfo))
.withDuration(700) //def
// .withEffect(effect) //def Effectstype.Slidetop
.withButton1Text(context.getString(R.string.btn_cancel)) //def gone
.isCancelableOnTouchOutside(false) //def | isCancelable(true)
// .setCustomView(R.layout.custom_view,v.getContext()) //.setCustomView(View or ResId,context)
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialogBuilder.dismiss();
}
})
.setOnDismissListener(listener);
dialogBuilder.show();
}
public static void dismissNiftyDialog() {
if (dialogBuilder != null) {
dialogBuilder.dismiss();
}
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/GpsUtils.java
================================================
package com.luo.bluetooth.utils;
import android.content.Context;
import android.location.LocationManager;
public class GpsUtils {
public static boolean isOPen(final Context context) {
LocationManager locationManager
= (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// 通过GPS卫星定位,定位级别可以精确到街(通过24颗卫星定位,在室外和空旷的地方定位准确、速度快)
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 通过WLAN或移动网络(3G/2G)确定的位置(也称作AGPS,辅助GPS定位。主要用于在室内或遮盖物(建筑群或茂密的深林等)密集的地方定位)
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (gps || network) {
return true;
}
return false;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/GsonUtils.java
================================================
package com.luo.bluetooth.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
/**
* Created by tony.luopeiqin on 2017/9/25.
*/
public class GsonUtils {
public static boolean isPrintException = true;
private static Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create(); ;
private GsonUtils() {
throw new Error("Do not need instantiate!");
}
private static final SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* Json数组转对象链表
*
* @param jsonArray
* @param cls
* @param
* @return
*/
public static List string2Objects(String jsonArray, Class cls) {
List res = new LinkedList();
//Json的解析类对象
JsonParser parser = new JsonParser();
//将JSON的String 转成一个JsonArray对象
JsonArray array = parser.parse(jsonArray).getAsJsonArray();
for (int i = 0; i < array.size(); i++) {
T t = gson.fromJson(array.get(i).toString(), cls);
// T t= JSONUtils.string2Obejct(array.get(i).toString(),cls);
res.add(t);
}
return res;
}
/**
* 从java对象转换为字符串
*/
public static String Object2String(Object obj) {
return gson.toJson(obj);
}
/**
* 从字符串转换为java对象
*/
public static T string2Object(String jsonStr, Class c) {
if (jsonStr.startsWith("[") && jsonStr.endsWith("]")) {
jsonStr = jsonStr.replace('[', '{').replace(']', '}');
}
T t = gson.fromJson(jsonStr, c);
return t;
}
public static String objToJson(Object obj) {
return gson.toJson(obj);
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/LogUtils.java
================================================
package com.luo.bluetooth.utils;
import android.util.Log;
import com.tencent.bugly.crashreport.BuglyLog;
import com.luo.bluetooth.BuildConfig;
public class LogUtils {
private static boolean isDebug = BuildConfig.DEBUG;
private LogUtils() {
}
public static void v(String tag, String msg) {
BuglyLog.v(tag, msg);
if (isDebug) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
BuglyLog.d(tag, msg);
if (isDebug) {
//信息太长,分段打印
//因为String的length是字符数量不是字节数量所以为了防止中文字符过多,
// 把4*1024的MAX字节打印长度改为2001字符数
int max_str_length = 2001 - tag.length();
//大于4000时
while (msg.length() > max_str_length) {
Log.d(tag, msg.substring(0, max_str_length));
msg = msg.substring(max_str_length);
}
//剩余部分
Log.d(tag, msg);
}
}
public static void i(String tag, String msg) {
BuglyLog.i(tag, msg);
if (isDebug) {
Log.i(tag, msg);
}
}
public static void w(String tag, String msg) {
BuglyLog.w(tag, msg);
if (isDebug) {
Log.w(tag, msg);
}
}
public static void e(String tag, String msg) {
BuglyLog.e(tag, msg);
if (isDebug) {
Log.e(tag, msg);
}
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/TextUtils.java
================================================
package com.luo.bluetooth.utils;
public class TextUtils {
public static boolean isEmpty(String dateStr) {
// TODO Auto-generated method stub
if (dateStr != null && dateStr.length() > 0) {
return false;
}
return true;
}
}
================================================
FILE: app/src/main/java/com/luo/bluetooth/utils/ToastUtil.java
================================================
package com.luo.bluetooth.utils;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
/**
* Toast 提示类
* Created by wangwentao on 2017/1/25.
* Toast统一管理类
*/
public class ToastUtil {
private static boolean isShow = true;//默认显示
private static Toast mToast = null;//全局唯一的Toast
/*private控制不应该被实例化*/
private ToastUtil() {
throw new UnsupportedOperationException("不能被实例化");
}
/**
* 全局控制是否显示Toast
* @param isShowToast
*/
public static void controlShow(boolean isShowToast){
isShow = isShowToast;
}
/**
* 取消Toast显示
*/
public void cancelToast() {
if(isShow && mToast != null){
mToast.cancel();
}
}
/**
* 短时间显示Toast
*
* @param context
* @param message
*/
public static void showShort(Context context, CharSequence message) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT);
} else {
mToast.setText(message);
}
mToast.show();
}
}
/**
* 短时间显示Toast
*
* @param context
* @param resId 资源ID:getResources().getString(R.string.xxxxxx);
*/
public static void showShort(Context context, int resId) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), resId, Toast.LENGTH_SHORT);
} else {
mToast.setText(resId);
}
mToast.show();
}
}
/**
* 长时间显示Toast
*
* @param context
* @param message
*/
public static void showLong(Context context, CharSequence message) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG);
} else {
mToast.setText(message);
}
mToast.show();
}
}
/**
* 长时间显示Toast
*
* @param context
* @param resId 资源ID:getResources().getString(R.string.xxxxxx);
*/
public static void showLong(Context context, int resId) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), resId, Toast.LENGTH_LONG);
} else {
mToast.setText(resId);
}
mToast.show();
}
}
/**
* 自定义显示Toast时间
*
* @param context
* @param message
* @param duration 单位:毫秒
*/
public static void show(Context context, CharSequence message, int duration) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, duration);
} else {
mToast.setText(message);
}
mToast.show();
}
}
/**
* 自定义显示Toast时间
*
* @param context
* @param resId 资源ID:getResources().getString(R.string.xxxxxx);
* @param duration 单位:毫秒
*/
public static void show(Context context, int resId, int duration) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), resId, duration);
} else {
mToast.setText(resId);
}
mToast.show();
}
}
/**
* 自定义Toast的View
* @param context
* @param message
* @param duration 单位:毫秒
* @param view 显示自己的View
*/
public static void customToastView(Context context, CharSequence message, int duration,View view) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, duration);
} else {
mToast.setText(message);
}
if(view != null){
mToast.setView(view);
}
mToast.show();
}
}
/**
* 自定义Toast的位置
* @param context
* @param message
* @param duration 单位:毫秒
* @param gravity
* @param xOffset
* @param yOffset
*/
public static void customToastGravity(Context context, CharSequence message, int duration,int gravity, int xOffset, int yOffset) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, duration);
} else {
mToast.setText(message);
}
mToast.setGravity(gravity, xOffset, yOffset);
mToast.show();
}
}
/**
* 自定义带图片和文字的Toast,最终的效果就是上面是图片,下面是文字
* @param context
* @param message
* @param iconResId 图片的资源id,如:R.drawable.icon
* @param duration
* @param gravity
* @param xOffset
* @param yOffset
*/
public static void showToastWithImageAndText(Context context, CharSequence message, int iconResId,int duration,int gravity, int xOffset, int yOffset) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, duration);
} else {
mToast.setText(message);
}
mToast.setGravity(gravity, xOffset, yOffset);
LinearLayout toastView = (LinearLayout) mToast.getView();
ImageView imageView = new ImageView(context.getApplicationContext());
imageView.setImageResource(iconResId);
toastView.addView(imageView, 0);
mToast.show();
}
}
/**
* 自定义Toast,针对类型CharSequence
* @param context
* @param message
* @param duration
* @param view
* @param isGravity true,表示后面的三个布局参数生效,false,表示不生效
* @param gravity
* @param xOffset
* @param yOffset
* @param isMargin true,表示后面的两个参数生效,false,表示不生效
* @param horizontalMargin
* @param verticalMargin
*/
public static void customToastAll(Context context, CharSequence message, int duration,View view,boolean isGravity,int gravity, int xOffset, int yOffset,boolean isMargin,float horizontalMargin, float verticalMargin) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), message, duration);
} else {
mToast.setText(message);
}
if(view != null){
mToast.setView(view);
}
if(isMargin){
mToast.setMargin(horizontalMargin, verticalMargin);
}
if(isGravity){
mToast.setGravity(gravity, xOffset, yOffset);
}
mToast.show();
}
}
/**
* 自定义Toast,针对类型resId
* @param context
* @param resId
* @param duration
* @param view :应该是一个布局,布局中包含了自己设置好的内容
* @param isGravity true,表示后面的三个布局参数生效,false,表示不生效
* @param gravity
* @param xOffset
* @param yOffset
* @param isMargin true,表示后面的两个参数生效,false,表示不生效
* @param horizontalMargin
* @param verticalMargin
*/
public static void customToastAll(Context context, int resId, int duration,View view,boolean isGravity,int gravity, int xOffset, int yOffset,boolean isMargin,float horizontalMargin, float verticalMargin) {
if (isShow){
if (mToast == null) {
mToast = Toast.makeText(context.getApplicationContext(), resId, duration);
} else {
mToast.setText(resId);
}
if(view != null){
mToast.setView(view);
}
if(isMargin){
mToast.setMargin(horizontalMargin, verticalMargin);
}
if(isGravity){
mToast.setGravity(gravity, xOffset, yOffset);
}
mToast.show();
}
}
}
================================================
FILE: app/src/main/res/anim/fade_in.xml
================================================
================================================
FILE: app/src/main/res/anim/fade_out.xml
================================================
================================================
FILE: app/src/main/res/drawable/btn_press.xml
================================================
================================================
FILE: app/src/main/res/drawable/btn_selector.xml
================================================
================================================
FILE: app/src/main/res/drawable/btn_unpress.xml
================================================
================================================
FILE: app/src/main/res/drawable/dialog_bg.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable/sel_white_gray.xml
================================================
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_search_device_list.xml
================================================
================================================
FILE: app/src/main/res/layout/dialog_layout.xml
================================================
================================================
FILE: app/src/main/res/layout/item_bluetooth_device_list.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/attrs.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#B3E5FC
#13619f
#1c82d4
#FF4081
#212121
#757575
#FFFFFF
#757575
#FFFFFF
@color/colorPrimary
#ffe74c3c
#FFFFFF
#11000000
#FFFFFFFF
#66000000
#22000000
#EAEAEA
#00a910
#E3E8ED
#000000
#FFFFFF
#f8f8ff
#808080
#dcdcdc
#ff0000
#ffa500
#008000
#0000ff
#00000000
#FFFFFF
#FFFFFF
#D0D0D0
#B6B6B6
#AEDEF4
#96BFD2
#DD6B55
#CD5B55
#F27474
#A5DC86
#33A5DC86
#F8BB86
#707f84
#323232
#a8e3ce
#dddddd
#d94130
#1fbaf3
#bfbfbf
#F0F0F0
================================================
FILE: app/src/main/res/values/strings.xml
================================================
蓝牙示例程序
正在搜索设备...
停止搜索设备
请选择已连接过的设备
设置备注名称
请选择要连接的设备
OK
Cancel
确定
取消
告警ID:
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/com/luo/bluetooth/ExampleUnitTest.java
================================================
package com.luo.bluetooth;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: bluetooth/.gitignore
================================================
/build
================================================
FILE: bluetooth/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
// compile 'com.android.support:appcompat-v7:25.1.0'
testImplementation files('libs/samsung_ble_sdk_200.jar')
'junit:junit:4.12'
implementation files('libs/com.broadcom.bt.jar')
implementation files('libs/samsung_ble_sdk_200.jar')
}
================================================
FILE: bluetooth/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: bluetooth/src/androidTest/java/com/stag/bluetooth/ExampleInstrumentedTest.java
================================================
package com.stag.bluetooth;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.stag.bluetooth.test", appContext.getPackageName());
}
}
================================================
FILE: bluetooth/src/main/AndroidManifest.xml
================================================
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/BluetoothController.java
================================================
package com.stag.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.TextUtils;
import com.stag.bluetooth.extend.BleService;
import com.stag.bluetooth.helper.BleHelper;
import com.stag.bluetooth.helper.BluetoothHelper;
import com.stag.bluetooth.helper.TraditionHelper;
import com.stag.bluetooth.helper.TraditionServerHelper;
import com.stag.bluetooth.protocol.Protocol;
import com.stag.bluetooth.util.Logs;
import java.util.LinkedList;
import java.util.List;
/**
* 蓝牙总控制
* Created by LPQ on 2017/11/14.
*/
public final class BluetoothController {
//Bluetooth type
public static final short TYPE_BLE = 1; // 客户端:低功耗蓝牙
public static final short TYPE_TRADITION = 2; // 客户端:传统蓝牙
public static final short TYPE_TRADITION_SERVER = 3; // 服务器:传统蓝牙
//Bluetooth scan filter
// public static final short SCAN_FILTER_ALL = 0;
// public static final short SCAN_FILTER_NAME_NON_NULL = 1;
// public static final short SCAN_FILTER_TYPE_MATCHING = 2;
private static BluetoothController controller;
private BluetoothHelper mHelper;
private Protocol mProtocol;
private short mBluetoothType;
private Context mContext;
private OnBluetoothScanListener mScanListener; //蓝牙扫描监听
private OnBluetoothConnectStateChangeListener mCurrentConnectlistener; //当前controller连接状态监听
private OnBluetoothConnectStateChangeListener mParamConnectlistener; //传进来的连接状态监听
private OnBluetoothStateChangeListener mBluetoothStateListener; //监听手机蓝牙状态
private List mDevices;
private boolean mScanRemoveRepeat; //搜索设备进行排重检查
private boolean isScanning;
private boolean isStartScanning;
public static BluetoothController getController(Context context) {
if (controller == null) {
synchronized (BluetoothController.class) {
if (controller == null)
if (context == null)
throw new NullPointerException("BluetoothController init fail, context is null!");
controller = new BluetoothController(context);
}
}
return controller;
}
private BluetoothController(Context context) {
mContext = context.getApplicationContext();
mDevices = new LinkedList();
mCurrentConnectlistener = new OnBluetoothConnectStateChangeListener() {
@Override
public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {
if (isSuccess) {
/*设置发送方法*/
setSendMethod();
/**启动蓝牙传输*/
BluetoothTransfer.getInstance().start();
}
if (mParamConnectlistener != null)
mParamConnectlistener.onBluetoothConnect(device, isSuccess);
}
@Override
public void onBluetoothDisconnect(BluetoothDevice device) {
BluetoothTransfer.getInstance().stop();
setProtocol(null);
if (mParamConnectlistener != null)
mParamConnectlistener.onBluetoothDisconnect(device);
}
};
setBluetoothType(TYPE_BLE);
}
public OnBluetoothConnectStateChangeListener getConnectStateChangeListener() {
return mParamConnectlistener;
}
public void registerConnectStateChangeListener(final OnBluetoothConnectStateChangeListener listener) {
mParamConnectlistener = listener;
}
public void unregisterConnectStateChangeListener() {
registerConnectStateChangeListener(null);
}
public OnBluetoothStateChangeListener getBluetoothStateChangeListener() {
return mBluetoothStateListener;
}
public void registerBluetoothStateChangeListener(OnBluetoothStateChangeListener listener) {
mBluetoothStateListener = listener;
}
public void unregisterBluetoothStateChangeListener(){
registerBluetoothStateChangeListener(null);
}
public OnBluetoothTransmitListener getTransmitListener() {
if (mHelper != null)
return mHelper.getTransmitListener();
return null;
}
public void registerTransmitListener(OnBluetoothTransmitListener transmitListener) {
// BleHelper.getInstance(mContext).setTransmitListener(transmitListener);
// TraditionHelper.getInstance(mContext).setTransmitListener(transmitListener);
mHelper.setTransmitListener(transmitListener);
}
public void unregisterTransmitListener(){
registerTransmitListener(null);
}
public void setBluetoothType(short bluetoothType) {
if (mBluetoothType == bluetoothType)
return;
mBluetoothType = bluetoothType;
if (mBluetoothType == TYPE_BLE) {
mHelper = BleHelper.getInstance(mContext);
} else if (mBluetoothType == TYPE_TRADITION){
mHelper = TraditionHelper.getInstance(mContext);
} else {
mHelper = TraditionServerHelper.getInstance(mContext);
}
mHelper.setProtocol(mProtocol);
mHelper.setConnectStateChangeListener(mCurrentConnectlistener);
setSendMethod();
}
public void setProtocol(Protocol protocol) {
if (mProtocol != null) {
mProtocol.destroy();
}
mProtocol = protocol;
if (mProtocol!=null)
mProtocol.initialize();
if (mHelper != null)
mHelper.setProtocol(protocol);
BluetoothTransfer.getInstance().setProtocol(mProtocol);
}
public Protocol getProtocol() {
return mProtocol;
}
/**
* 是否已经连接设备
*/
public boolean isConnected() {
if (mHelper != null)
return mHelper.isConnected();
return false;
}
public boolean isConnecting(){
if (mHelper != null)
return mHelper.isConnecting();
return false;
}
public boolean isScanning(){
return isScanning;
}
/**
* 获取蓝牙类型,BLE或者传统
*/
public short getBluetoothType() {
return mBluetoothType;
}
public boolean setBleHighConnectionPriority(boolean flag){
boolean res = false;
if (mHelper!=null && mBluetoothType==TYPE_BLE)
res = ((BleHelper) mHelper).setHighConnectionPriority(flag);
return res;
}
public boolean setBleHighSpeedMode(boolean flag){
boolean res = false;
if (mHelper!=null && mBluetoothType==TYPE_BLE)
res = ((BleHelper) mHelper).setHighSpeedMode(flag);
return res;
}
public boolean isBleHighSpeedMode(){
boolean res = false;
if (mHelper!=null && mBluetoothType==TYPE_BLE)
res = ((BleHelper) mHelper).isHighSpeedMode();
return res;
}
/**
* 设置发送方法
*/
private void setSendMethod() {
BluetoothTransfer.getInstance().setSend(new BluetoothTransfer.SendMethod() {
@Override
public void send(byte[] data) {
mHelper.send(data);
}
});
}
public void sendData(byte[] data) {
if (mHelper != null) {
mHelper.send(data);
}
}
/**
* 连接设备,必须先设置Protocol或者使用connect(String address, Protocol protocol)
*/
public void connect(String address) {
connect(address, mProtocol);
}
public void connect(String address, Protocol protocol){
setProtocol(protocol);
mHelper.connect(address);
}
/**
* 断开设备
*/
public void disconnect() {
mHelper.disconnect();
unregisterBluetoothBroadcast();
BluetoothTransfer.getInstance().stop();
}
/**
* 扫描设备
*/
public void startScan(OnBluetoothScanListener listener) {
if (isScanning) {
isStartScanning = true;
stopScan();
}
if (mScanListener!=listener)
mScanListener = listener;
registerBluetoothBroadcast();
isScanning = true;
mHelper.startScan();
// BleHelper.getInstance(mContext).startScan();
// TraditionHelper.getInstance(mContext).startScan();
}
/**
* 停止扫描
*/
public void stopScan() {
if (isScanning) {
mScanListener = null;//防止DeviceListActivity内存泄漏
mHelper.stopScan();
// BleHelper.getInstance(mContext).stopScan();
// TraditionHelper.getInstance(mContext).stopScan();
if (isScanRemoveRepeat())
mDevices.clear();
isScanning = false;
}
}
/*private void stopScan(boolean isSelf){
if (isSelf){
stopScan();
}
}*/
private void registerBluetoothBroadcast() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BleService.BLE_DEVICE_FOUND);
filter.addAction(BleHelper.BLE_SCAN_FINISH);
mContext.registerReceiver(mReceiver, filter);
}
private void unregisterBluetoothBroadcast() {
if (mReceiver != null) {
try {
mContext.unregisterReceiver(mReceiver);
} catch (Exception e) {
}
}
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = null;
int rssi;
switch (intent.getAction()){
case BluetoothAdapter.ACTION_STATE_CHANGED:
int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
switch (blueState) {
// case BluetoothAdapter.STATE_TURNING_ON:
case BluetoothAdapter.STATE_ON:
if (mBluetoothStateListener != null)
mBluetoothStateListener.onBluetoothOpen();
break;
// case BluetoothAdapter.STATE_TURNING_OFF:
case BluetoothAdapter.STATE_OFF:
if (mBluetoothStateListener != null)
mBluetoothStateListener.onBluetoothClose();
break;
default:
break;
}
break;
case BluetoothDevice.ACTION_ACL_DISCONNECTED:
Logs.d("LPQ", "discontect from broadcast");
if (mHelper != null)
mHelper.disconnect();
break;
case BluetoothDevice.ACTION_FOUND:
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
rssi = (int) intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, (short) 0);
handleScanFindResult(device, rssi, false);
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
if (mScanListener != null)
mScanListener.onBluetoothScanFinish();
if (isStartScanning){
isStartScanning = false;
}else {
stopScan();
}
break;
case BleService.BLE_DEVICE_FOUND:
device = intent.getParcelableExtra(BleService.EXTRA_DEVICE);
rssi = intent.getIntExtra(BleService.EXTRA_RSSI, 0);
handleScanFindResult(device, rssi, true);
break;
case BleHelper.BLE_SCAN_FINISH:
//因为BLE的扩展SDK没有这个,这个是另外加的,由BleHelper发送
if (mScanListener != null)
mScanListener.onBluetoothScanFinish();
break;
default:break;
}
}
};
/**
* 处理扫描发现结果
*/
private void handleScanFindResult(BluetoothDevice device, int rssi, boolean isBle) {
if (device == null) {
return;
}
if (TextUtils.isEmpty(device.getName())) {
return;
}
if (mScanRemoveRepeat) {
for (BluetoothDevice m : mDevices) {
if (m.getAddress().equals(device.getAddress())) {
return;
}
}
mDevices.add(device);
}
if (mScanListener != null) {
mScanListener.onBluetoothScanFindDevice(device, rssi, isBle);
}
}
public void setScanRemoveRepeat(boolean b) {
mScanRemoveRepeat = b;
}
public boolean isScanRemoveRepeat() {
return mScanRemoveRepeat;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/BluetoothDispatch.java
================================================
package com.stag.bluetooth;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 派遣所有回调的事件,根据Task分发到主线程或者子线程,建立线程池分发执行
* Created by LPQ on 2016/11/18.
*/
public final class BluetoothDispatch {
private static BluetoothDispatch instance;
private ExecutorService mCatchThreadPool;
private Handler mMainHandler;
public static BluetoothDispatch getInstance(){
if (instance==null){
synchronized (BluetoothDispatch.class){
if (instance==null)
instance = new BluetoothDispatch();
}
}
return instance;
}
public void start(){
if (mCatchThreadPool ==null){
mCatchThreadPool = Executors.newCachedThreadPool();
}
mMainHandler = new Handler(Looper.getMainLooper());
}
public void stop(){
if (mCatchThreadPool !=null){
mCatchThreadPool.shutdownNow();
mCatchThreadPool = null;
}
mMainHandler = null;
}
public void dispatch(final Callback callback, boolean runInMainThread){
if (callback==null) return;
if (runInMainThread){
mMainHandler.post(new Runnable() {
@Override
public void run() {
callback.callback();
}
});
}else {
if (mCatchThreadPool !=null){
mCatchThreadPool.execute(new Runnable() {
@Override
public void run() {
callback.callback();
}
});
}
}
}
/**
* 以防扩展
* */
public interface Callback{
void callback();
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/BluetoothTask.java
================================================
package com.stag.bluetooth;
import com.stag.bluetooth.packet.Packet;
/**
* 蓝牙发送任务
* Created by Bruce on 2016/11/15.
*/
public final class BluetoothTask {
private static int DEFAULT_TRY_COUNT = 2;
private static int DEFAULT_TIMEOUT = 3000; //默认超时时间,毫秒级
private static boolean DEFAULT_MAIN_THREAD = false;
private int tryCount; //尝试发送次数
private long timeout; //每次重发的超时时间,毫秒级
private long sentTime; //至少在应用层真正被发送出去的时间
private T packet; //数据包
// private int priority; //优先级,暂时业务不需要
private boolean resultCallbackInMainThread; //结果回调是否需要执行在主线程上
private OnResultListener onResult; //响应回调
public BluetoothTask(E packet) {
this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, DEFAULT_MAIN_THREAD, null);
}
public BluetoothTask(E packet, OnResultListener onResult) {
this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, DEFAULT_MAIN_THREAD, onResult);
}
public BluetoothTask(E packet, boolean resultCallbackInMainThread, OnResultListener onResult) {
this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, resultCallbackInMainThread, onResult);
}
public BluetoothTask(int tryCount, int timeout, E packet, OnResultListener onResult) {
this(tryCount, timeout, packet, DEFAULT_MAIN_THREAD, onResult);
}
public BluetoothTask(int tryCount, int timeout, E packet, boolean resultCallbackInMainThread, OnResultListener onResult) {
this.tryCount = tryCount;
setTimeout(timeout);
this.packet = packet;
this.resultCallbackInMainThread = resultCallbackInMainThread;
this.onResult = onResult;
}
public boolean isTimeout() {
long t = (System.nanoTime() - sentTime);
return t > timeout && sentTime != 0;
}
/**
* 异步发送
* @return
*/
public void send() {
BluetoothTransfer.getInstance().addSendTask(this);
}
/**
* 阻塞发送,返回数据部分的字节数组
* @return
*/
public byte[] sendBySync2() {
if (BluetoothTransfer.getInstance().isStop())
return null;
final byte[][] res = new byte[1][];
synchronized (res) {
setOnResult(new OnDataResultListener() {
@Override
public void onResult(boolean isTimeout, byte[] data) {
synchronized (res) {
res[0] = data;
res.notify();
}
}
});
send();
try {
res.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return res[0];
}
/**
* 阻塞发送,返回完整的包
* @return
*/
public T sendBySync() {
if (BluetoothTransfer.getInstance().isStop())
return null;
final T[] res = (T[]) new Packet[1];
synchronized (this) {
setOnResult(new OnResultListener() {
@Override
public void onResult(boolean isTimeout, T packet) {
synchronized (res) {
res[0] = packet;
res.notify();
}
}
});
send();
try {
res.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return res[0];
}
public int getTryCount() {
return tryCount;
}
public BluetoothTask setTryCount(int tryCount) {
this.tryCount = tryCount;
return this;
}
public long getTimeout() {
return timeout / 1000000;
}
public BluetoothTask setTimeout(long timeout) {
this.timeout = timeout * 1000000;
return this;
}
public long getSentTime() {
return sentTime;
}
BluetoothTask setSentTime(long sendTime) {
this.sentTime = sendTime;
return this;
}
public Packet getPacket() {
return packet;
}
public BluetoothTask setPacket(T packet) {
this.packet = packet;
return this;
}
public boolean isResultCallbackInMainThread() {
return resultCallbackInMainThread;
}
public BluetoothTask setResultCallbackInMainThread(boolean resultCallbackInMainThread) {
this.resultCallbackInMainThread = resultCallbackInMainThread;
return this;
}
public OnResultListener getOnResult() {
return onResult;
}
public BluetoothTask setOnResult(OnResultListener onResult) {
this.onResult = onResult;
return this;
}
/**
* 只获取数据部分
*/
public abstract static class OnDataResultListener implements OnResultListener {
@Override
public void onResult(boolean isTimeout, Packet packet) {
onResult(isTimeout, packet == null ? null : packet.getData());
}
public abstract void onResult(boolean isTimeout, byte[] data);
}
/**
* 返回整个回应包
*/
public interface OnResultListener {
void onResult(boolean isTimeout, T packet);
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/BluetoothTransfer.java
================================================
package com.stag.bluetooth;
import com.stag.bluetooth.protocol.ParseResult;
import com.stag.bluetooth.protocol.Protocol;
import com.stag.bluetooth.util.ByteUtils;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList;
/**
* 蓝牙传输控制类
* 目前不支持流水号,如果同一条指令并发发送,采取FIFO的形式
* Created by LPQ on 2016/11/14.
*/
public final class BluetoothTransfer {
private static final short RECV_BUFFER_SIZE = 1024; //接收缓冲区大小
private static final short TIMEOUT_CHECK_ACCURACY = 100; //超时检查的精确度,即超时时间的误差范围,毫秒级
public static BluetoothTransfer instance;
private Protocol mProtocol; //所使用协议
private SendMethod send; //发送蓝牙数据的方法,发送时必须设置好Task的发送时间
private LinkedList sendList; //发送任务执行队列,暂时先自行管理同步
private LinkedList waitRespondList; //待响应队列,暂时先自行管理同步
private ByteBuffer recvBuf; //接收缓冲区
private Thread sendThread; //负责发送
private Thread recvHandlerThread; //负责处理接收到的数据
private Thread timeoutCheckThread;
private boolean isResume;
private Object recvLock, sendLock, waitRespondLock;
private BluetoothDispatch dispatch;
public static BluetoothTransfer getInstance(){
if (instance==null){
synchronized (BluetoothTransfer.class){
if (instance==null)
instance = new BluetoothTransfer();
}
}
return instance;
}
private BluetoothTransfer(){
sendList = new LinkedList<>();
waitRespondList = new LinkedList<>();
recvBuf = ByteBuffer.allocate(RECV_BUFFER_SIZE);
recvLock = new Object();
sendLock = new Object();
waitRespondLock = new Object();
dispatch = BluetoothDispatch.getInstance();
}
/**
* 启动蓝牙传输
* */
public void start(){
stop();
dispatch.start();
isResume = true;
startSendQueue();
startTimeoutCheck();
startRecvHandlerQueue();
}
/**
* 停止蓝牙传输
* */
public void stop(){
dispatch.stop();
isResume = false;
stopSendQueue();
stopRecvHandlerQueue();
stopTimeoutCheck();
synchronized (sendLock){
for (BluetoothTask task:sendList){
if (task.getOnResult()!=null)
task.getOnResult().onResult(true, null);
}
sendList.clear();
}
synchronized (waitRespondLock){
for (BluetoothTask task:waitRespondList){
if (task.getOnResult()!=null)
task.getOnResult().onResult(true, null);
}
waitRespondList.clear();
}
synchronized (recvLock){
recvBuf.clear();
}
}
/**
* 蓝牙发送数据
* */
private void startSendQueue(){
sendThread = new Thread(){
@Override
public void run() {
super.run();
while (isResume){
BluetoothTask task;
synchronized (sendLock){
while (sendList.isEmpty()){
try {
sendLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isResume)
return;
/*检查和处理超时的已发送的待响应任务*/
// checkTimeoutTask();
}
/*发送数据,把task放入待响应队列*/
task = sendList.removeFirst();
}
if (task.getOnResult()!=null){
synchronized (waitRespondLock){
waitRespondList.add(task);
}
}
send.send(mProtocol.packetToBytes(task.getPacket()));
task.setSentTime(System.nanoTime());
}
}
};
sendThread.start();
}
/**
* 蓝牙收到数据后的处理
* */
private void startRecvHandlerQueue(){
recvHandlerThread = new Thread(){
@Override
public void run() {
super.run();
while (isResume){
synchronized (recvLock){
while (recvBuf.position()==0){
try {
recvLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isResume)
return;
}
final ParseResult result = mProtocol.parse(ByteUtils.getBytesWithBuffer(recvBuf));
recvBuf.clear();
switch (result.getType()){
case RESPOND://响应事件
synchronized (waitRespondLock){
Iterator iterator = waitRespondList.iterator();
while (iterator.hasNext()){
final BluetoothTask task = iterator.next();
if (task.getPacket().match(result.getPacket())){
dispatch.dispatch(new BluetoothDispatch.Callback() {
@Override
public void callback() {
if (task.getOnResult()!=null)
task.getOnResult().onResult(false, result.getPacket());
}
}, task.isResultCallbackInMainThread());
iterator.remove();
break;
}
}
}
break;
case ACTIVE://主动事件
dispatch.dispatch(result.getCallback(), false);
break;
case INCOMPLETE://非完整一帧
//好像暂时没啥事干,多余的数据在对应协议里会保存以备下次解析
break;
default:break;
}
}
}
}
};
recvHandlerThread.start();
}
private void startTimeoutCheck(){
timeoutCheckThread = new Thread(){
@Override
public void run() {
super.run();
while (isResume){
try {
Thread.sleep(TIMEOUT_CHECK_ACCURACY);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isResume)
return;
synchronized (waitRespondLock){
Iterator iterator = waitRespondList.iterator();
while (iterator.hasNext()){
final BluetoothTask task = iterator.next();
if (task.isTimeout()){
if (task.getTryCount()>1){
task.setTryCount(task.getTryCount()-1);
addSendTask(task);
}else {
BluetoothDispatch.getInstance().dispatch(new BluetoothDispatch.Callback() {
@Override
public void callback() {
task.getOnResult().onResult(true, null);
}
}, task.isResultCallbackInMainThread());
}
iterator.remove();
}else {
// Logs.d("LPQ", "没有timeout");
}
}
}
}
}
};
timeoutCheckThread.start();
}
private void stopSendQueue(){
if (sendThread!=null){
sendThread.interrupt();
sendThread = null;
}
}
private void stopRecvHandlerQueue(){
if (recvHandlerThread !=null){
recvHandlerThread.interrupt();
recvHandlerThread = null;
}
}
private void stopTimeoutCheck(){
if (timeoutCheckThread !=null){
timeoutCheckThread.interrupt();
timeoutCheckThread = null;
}
}
public boolean addSendTask(BluetoothTask task){
if (isResume){
synchronized (sendLock){
task.setSentTime(0);
sendList.add(task);
sendLock.notify();
}
}else {
if (task.getOnResult()!=null)
task.getOnResult().onResult(true, null);
}
return isResume;
}
public void addRecvData(byte[] data){
synchronized (recvLock){
recvBuf.put(data);
recvLock.notify();
}
}
public boolean isStop(){
return !isResume;
}
/**
* 检查和处理超时的发送任务
* */
private void checkTimeoutTask(){
synchronized (waitRespondLock){
Iterator iterator = waitRespondList.iterator();
while (iterator.hasNext()){
final BluetoothTask task = iterator.next();
if (task.isTimeout()){
if (task.getTryCount()>1){
task.setTryCount(task.getTryCount()-1);
sendList.add(task);
}else {
BluetoothDispatch.getInstance().dispatch(new BluetoothDispatch.Callback() {
@Override
public void callback() {
task.getOnResult().onResult(true, null);
}
}, task.isResultCallbackInMainThread());
}
iterator.remove();
}
}
}
}
public Protocol getProtocol() {
return mProtocol;
}
public void setProtocol(Protocol mProtocol) {
this.mProtocol = mProtocol;
}
public SendMethod getSend() {
return send;
}
public void setSend(SendMethod send) {
this.send = send;
}
public interface SendMethod{
void send(byte[] data);
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothConnectStateChangeListener.java
================================================
package com.stag.bluetooth;
import android.bluetooth.BluetoothDevice;
/**
* 连接状态监听
* Created by Administrator on 2016/11/17.
*/
public interface OnBluetoothConnectStateChangeListener {
/**
* 蓝牙设备连接回调
* @param device 蓝牙设备
* @param isSuccess 是否成功
* */
void onBluetoothConnect(BluetoothDevice device, boolean isSuccess);
/**
* 蓝牙设备断开回调
* @param device 蓝牙设备
* */
void onBluetoothDisconnect(BluetoothDevice device);
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothScanListener.java
================================================
package com.stag.bluetooth;
import android.bluetooth.BluetoothDevice;
/**
* 蓝牙扫描监听
* Created by Administrator on 2016/11/25.
*/
public interface OnBluetoothScanListener {
void onBluetoothScanFindDevice(BluetoothDevice device, int rssi, boolean isBle);
void onBluetoothScanFinish();
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothStateChangeListener.java
================================================
package com.stag.bluetooth;
/**
* 监听手机蓝牙状态
* Created by Administrator on 2016/11/18.
*/
public interface OnBluetoothStateChangeListener {
/**手机蓝牙打开*/
void onBluetoothOpen();
/**手机蓝牙关闭*/
void onBluetoothClose();
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothTransmitListener.java
================================================
package com.stag.bluetooth;
/**
* 蓝牙数据收发监听
* Created by Administrator on 2016/12/21.
*/
public interface OnBluetoothTransmitListener {
void onBluetoothSendData(byte[] data);
void onBluetoothRecvData(byte[] data);
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/AndroidBle.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import com.stag.bluetooth.extend.BleRequest.RequestType;
//import org.apache.commons.codec.binary.Hex;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@SuppressLint("NewApi")
public class AndroidBle implements IBle, IBleRequestHandler {
protected static final String TAG = "blelib";
private BleService mService;
private BluetoothAdapter mBtAdapter;
private Map mBluetoothGatts;
// private BTQuery btQuery;
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
mService.bleDeviceFound(device, rssi, scanRecord,
BleService.DEVICE_SOURCE_SCAN);
}
};
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
String address = gatt.getDevice().getAddress();
Log.d(TAG, "onConnectionStateChange " + address + " status "
+ status + " newState " + newState);
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect(address);
mService.bleGattDisConnected(address);
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
mService.bleGattConnected(gatt.getDevice());
mService.addBleRequest(new BleRequest(
RequestType.DISCOVER_SERVICE, address));
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mService.bleGattDisConnected(address);
disconnect(address);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
String address = gatt.getDevice().getAddress();
Log.d(TAG, "onServicesDiscovered " + address + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.DISCOVER_SERVICE, false);
return;
}
mService.bleServiceDiscovered(gatt.getDevice().getAddress());
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
String address = gatt.getDevice().getAddress();
Log.d(TAG, "onCharacteristicRead " + address + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.READ_CHARACTERISTIC, false);
return;
}
// Log.d(TAG, "data " + characteristic.getStringValue(0));
mService.bleCharacteristicRead(gatt.getDevice().getAddress(),
characteristic.getUuid().toString(), status,
characteristic.getValue());
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
String address = gatt.getDevice().getAddress();
// Log.d(TAG, "onCharacteristicChanged " + address);
// Log.d(TAG, new String(Hex.encodeHex(characteristic.getValue())));
mService.bleCharacteristicChanged(address, characteristic.getUuid()
.toString(), characteristic.getValue());
}
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
String address = gatt.getDevice().getAddress();
Log.d(TAG, "onCharacteristicWrite " + address + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.WRITE_CHARACTERISTIC, false);
return;
}
mService.bleCharacteristicWrite(gatt.getDevice().getAddress(),
characteristic.getUuid().toString(), status);
};
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
String address = gatt.getDevice().getAddress();
Log.d(TAG, "onDescriptorWrite " + address + " status " + status);
BleRequest request = mService.getCurrentRequest();
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION
|| request.type == RequestType.CHARACTERISTIC_INDICATION
|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.CHARACTERISTIC_NOTIFICATION, false);
return;
}
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), true,
status);
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
mService.bleCharacteristicIndication(address, descriptor
.getCharacteristic().getUuid().toString(), status);
} else {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), false,
status);
}
return;
}
};
};
public AndroidBle(BleService service) {
mService = service;
// btQuery = BTQuery.getInstance();
if (!mService.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE)) {
mService.bleNotSupported();
return;
}
final BluetoothManager bluetoothManager = (BluetoothManager) mService
.getSystemService(Context.BLUETOOTH_SERVICE);
mBtAdapter = bluetoothManager.getAdapter();
if (mBtAdapter == null) {
mService.bleNoBtAdapter();
}
mBluetoothGatts = new HashMap();
}
@Override
public void startScan() {
mBtAdapter.startLeScan(mLeScanCallback);
}
@Override
public void stopScan() {
mBtAdapter.stopLeScan(mLeScanCallback);
}
@Override
public boolean adapterEnabled() {
if (mBtAdapter != null) {
return mBtAdapter.isEnabled();
}
return false;
}
@Override
public boolean connect(String address) {
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
BluetoothGatt gatt = device.connectGatt(mService, false, mGattCallback);
if (gatt == null) {
mBluetoothGatts.remove(address);
return false;
} else {
// TODO: if state is 141, it can be connected again after about 15
// seconds
mBluetoothGatts.put(address, gatt);
return true;
}
}
@Override
public void disconnect(String address) {
if (mBluetoothGatts.containsKey(address)) {
BluetoothGatt gatt = mBluetoothGatts.remove(address);
if (gatt != null) {
gatt.disconnect();
gatt.close();
}
}
}
@Override
public ArrayList getServices(String address) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null) {
return null;
}
ArrayList list = new ArrayList();
List services = gatt.getServices();
for (BluetoothGattService s : services) {
BleGattService service = new BleGattService(s);
// service.setInfo(btQuery.getGattServiceInfo(s.getUuid()));
list.add(service);
}
return list;
}
@Override
public boolean requestReadCharacteristic(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
mService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,
gatt.getDevice().getAddress(), characteristic));
return true;
}
public boolean readCharacteristic(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null) {
return false;
}
return gatt.readCharacteristic(characteristic.getGattCharacteristicA());
}
@Override
public boolean discoverServices(String address) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null) {
return false;
}
boolean ret = gatt.discoverServices();
if (!ret) {
disconnect(address);
}
return ret;
}
@Override
public BleGattService getService(String address, UUID uuid) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null) {
return null;
}
BluetoothGattService service = gatt.getService(uuid);
if (service == null) {
return null;
} else {
return new BleGattService(service);
}
}
@Override
public boolean requestCharacteristicNotification(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_NOTIFICATION, gatt.getDevice()
.getAddress(), characteristic));
return true;
}
@Override
public boolean characteristicNotification(String address,
BleGattCharacteristic characteristic) {
BleRequest request = mService.getCurrentRequest();
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
boolean enable = true;
if (request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {
enable = false;
}
BluetoothGattCharacteristic c = characteristic.getGattCharacteristicA();
if (!gatt.setCharacteristicNotification(c, enable)) {
return false;
}
BluetoothGattDescriptor descriptor = c
.getDescriptor(BleService.DESC_CCC);
if (descriptor == null) {
return false;
}
byte[] val_set = null;
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
val_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
val_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
val_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
if (!descriptor.setValue(val_set)) {
return false;
}
return gatt.writeDescriptor(descriptor);
}
@Override
public boolean requestWriteCharacteristic(String address,
BleGattCharacteristic characteristic, String remark) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
mService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,
gatt.getDevice().getAddress(), characteristic, remark));
return true;
}
@Override
public boolean writeCharacteristic(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null) {
return false;
}
// Log.d("blelib", new String(Hex.encodeHex(characteristic.getGattCharacteristicA().getValue())));
return gatt
.writeCharacteristic(characteristic.getGattCharacteristicA());
}
@Override
public boolean requestConnect(String address) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt != null && gatt.getServices().size() == 0) {
return false;
}
mService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));
return true;
}
@Override
public String getBTAdapterMacAddr() {
if (mBtAdapter != null) {
return mBtAdapter.getAddress();
}
return null;
}
@Override
public boolean requestIndication(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_INDICATION, gatt.getDevice()
.getAddress(), characteristic));
return true;
}
@Override
public boolean requestStopNotification(String address,
BleGattCharacteristic characteristic) {
BluetoothGatt gatt = mBluetoothGatts.get(address);
if (gatt == null || characteristic == null) {
return false;
}
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_NOTIFICATION, gatt.getDevice()
.getAddress(), characteristic));
return true;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/BleGattCharacteristic.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.annotation.SuppressLint;
import com.stag.bluetooth.extend.BleService.BLESDK;
import java.util.UUID;
@SuppressLint("NewApi")
public class BleGattCharacteristic {
public static final int PROPERTY_READ = 2;
public static final int PROPERTY_WRITE = 8;
public static final int PROPERTY_NOTIFY = 16;
public static final int PROPERTY_INDICATE = 32;
/**
* Characteristic value format type uint8
*/
public static final int FORMAT_UINT8 = 0x11;
/**
* Characteristic value format type uint16
*/
public static final int FORMAT_UINT16 = 0x12;
/**
* Characteristic value format type uint24 Note: this is not a standard data
* type!
*/
public static final int FORMAT_UINT24 = 0x13;
/**
* Characteristic value format type uint32
*/
public static final int FORMAT_UINT32 = 0x14;
/**
* Characteristic value format type sint8
*/
public static final int FORMAT_SINT8 = 0x21;
/**
* Characteristic value format type sint16
*/
public static final int FORMAT_SINT16 = 0x22;
/**
* Characteristic value format type sint32
*/
public static final int FORMAT_SINT32 = 0x24;
/**
* Characteristic value format type sfloat (16-bit float)
*/
public static final int FORMAT_SFLOAT = 0x32;
/**
* Characteristic value format type float (32-bit float)
*/
public static final int FORMAT_FLOAT = 0x34;
private android.bluetooth.BluetoothGattCharacteristic mGattCharacteristicA;
private com.broadcom.bt.gatt.BluetoothGattCharacteristic mGattCharacteristicB;
private com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic mGattCharacteristicS;
private BLESDK mBleSDK;
private String name;
public BleGattCharacteristic(android.bluetooth.BluetoothGattCharacteristic c) {
mBleSDK = BLESDK.ANDROID;
setGattCharacteristicA(c);
initInfo();
}
public BleGattCharacteristic(
com.broadcom.bt.gatt.BluetoothGattCharacteristic c) {
mBleSDK = BLESDK.BROADCOM;
setGattCharacteristicB(c);
}
public BleGattCharacteristic(
com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c) {
mBleSDK = BLESDK.SAMSUNG;
setGattCharacteristicS(c);
}
private void initInfo() {
name = "Unknown characteristic";
}
public UUID getUuid() {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().getUuid();
} else if (mBleSDK == BLESDK.BROADCOM) {
return getGattCharacteristicB().getUuid();
} else if (mBleSDK == BLESDK.SAMSUNG) {
return getGattCharacteristicS().getUuid();
}
return null;
}
protected android.bluetooth.BluetoothGattCharacteristic getGattCharacteristicA() {
return mGattCharacteristicA;
}
public int getProperties() {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().getProperties();
} else if (mBleSDK == BLESDK.BROADCOM) {
return getGattCharacteristicB().getProperties();
} else if (mBleSDK == BLESDK.SAMSUNG) {
return getGattCharacteristicS().getProperties();
}
return 0;
}
protected com.broadcom.bt.gatt.BluetoothGattCharacteristic getGattCharacteristicB() {
return mGattCharacteristicB;
}
protected void setGattCharacteristicB(
com.broadcom.bt.gatt.BluetoothGattCharacteristic mBCGattCharacteristic) {
this.mGattCharacteristicB = mBCGattCharacteristic;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean setValue(byte[] val) {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().setValue(val);
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.setValue(val);
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.setValue(val);
}
return false;
}
public byte[] getValue() {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().getValue();
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.getValue();
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.getValue();
}
return null;
}
public boolean setValue(int value, int formatType, int offset) {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().setValue(value, formatType, offset);
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.setValue(value, formatType, offset);
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.setValue(value, formatType, offset);
}
return false;
}
public boolean setValue(int mantissa, int exponent, int formatType,
int offset) {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().setValue(mantissa, exponent,
formatType, offset);
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.setValue(mantissa, exponent,
formatType, offset);
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.setValue(mantissa, exponent,
formatType, offset);
}
return false;
}
public boolean setValue(String value) {
return setValue(value.getBytes());
}
public String getStringValue(int offset) {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().getStringValue(offset);
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.getStringValue(offset);
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.getStringValue(offset);
}
return null;
}
public Float getFloatValue(int formatType, int offset) {
if (mBleSDK == BLESDK.ANDROID) {
return getGattCharacteristicA().getFloatValue(formatType, offset);
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattCharacteristicS.getFloatValue(formatType, offset);
} else if (mBleSDK == BLESDK.BROADCOM) {
return mGattCharacteristicB.getFloatValue(formatType, offset);
}
return null;
}
public Integer getIntValue(int formatType, int offset) {
if (mBleSDK == BLESDK.ANDROID) {
if (formatType == FORMAT_UINT24) {
byte[] value = getGattCharacteristicA().getValue();
return byte2uint24(offset, value);
} else {
return getGattCharacteristicA().getIntValue(formatType, offset);
}
} else if (mBleSDK == BLESDK.SAMSUNG) {
if (formatType == FORMAT_UINT24) {
byte[] value = mGattCharacteristicS.getValue();
return byte2uint24(offset, value);
} else {
return mGattCharacteristicS.getIntValue(formatType, offset);
}
} else if (mBleSDK == BLESDK.BROADCOM) {
if (formatType == FORMAT_UINT24) {
byte[] value = mGattCharacteristicB.getValue();
return byte2uint24(offset, value);
} else {
return mGattCharacteristicB.getIntValue(formatType, offset);
}
}
return null;
}
private Integer byte2uint24(int offset, byte[] value) {
if ((offset + 3) > value.length)
return null;
return Integer.valueOf((value[offset] & 0xFF)
| (value[offset + 1] & 0xFF) << 8
| (value[offset + 2] & 0xFF) << 16);
}
protected com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic getGattCharacteristicS() {
return mGattCharacteristicS;
}
protected void setGattCharacteristicS(
com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic mSSGattCharacteristic) {
this.mGattCharacteristicS = mSSGattCharacteristic;
}
protected void setGattCharacteristicA(
android.bluetooth.BluetoothGattCharacteristic mGattCharacteristicA) {
this.mGattCharacteristicA = mGattCharacteristicA;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/BleGattService.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothGattCharacteristic;
import com.stag.bluetooth.extend.BleService.BLESDK;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@SuppressLint("NewApi")
public class BleGattService {
private BLESDK mBleSDK;
private com.samsung.android.sdk.bt.gatt.BluetoothGattService mGattServiceS;
private com.broadcom.bt.gatt.BluetoothGattService mGattServiceB;
private android.bluetooth.BluetoothGattService mGattServiceA;
private String mName;
public BleGattService(com.samsung.android.sdk.bt.gatt.BluetoothGattService s) {
mBleSDK = BLESDK.SAMSUNG;
mGattServiceS = s;
initInfo();
}
public BleGattService(com.broadcom.bt.gatt.BluetoothGattService s) {
mBleSDK = BLESDK.BROADCOM;
mGattServiceB = s;
initInfo();
}
public BleGattService(android.bluetooth.BluetoothGattService s) {
mBleSDK = BLESDK.ANDROID;
mGattServiceA = s;
initInfo();
}
private void initInfo() {
mName = "Unknown Service";
}
public UUID getUuid() {
if (mBleSDK == BLESDK.BROADCOM) {
return mGattServiceB.getUuid();
} else if (mBleSDK == BLESDK.SAMSUNG) {
return mGattServiceS.getUuid();
} else if (mBleSDK == BLESDK.ANDROID) {
return mGattServiceA.getUuid();
}
return null;
}
public List getCharacteristics() {
ArrayList list = new ArrayList();
if (mBleSDK == BLESDK.BROADCOM) {
for (com.broadcom.bt.gatt.BluetoothGattCharacteristic c : mGattServiceB
.getCharacteristics()) {
list.add(new BleGattCharacteristic(c));
}
} else if (mBleSDK == BLESDK.SAMSUNG) {
for (Object o : mGattServiceS.getCharacteristics()) {
com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c = (com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic) o;
list.add(new BleGattCharacteristic(c));
}
} else if (mBleSDK == BLESDK.ANDROID) {
for (BluetoothGattCharacteristic c : mGattServiceA
.getCharacteristics()) {
list.add(new BleGattCharacteristic(c));
}
}
return list;
}
public BleGattCharacteristic getCharacteristic(UUID uuid) {
if (mBleSDK == BLESDK.ANDROID) {
BluetoothGattCharacteristic c = mGattServiceA
.getCharacteristic(uuid);
if (c != null) {
return new BleGattCharacteristic(c);
}
} else if (mBleSDK == BLESDK.SAMSUNG) {
com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c = mGattServiceS
.getCharacteristic(uuid);
if (c != null) {
return new BleGattCharacteristic(c);
}
} else if (mBleSDK == BLESDK.BROADCOM) {
com.broadcom.bt.gatt.BluetoothGattCharacteristic c = mGattServiceB
.getCharacteristic(uuid);
if (c != null) {
return new BleGattCharacteristic(c);
}
}
return null;
}
public void setInfo(JSONObject info) {
if (info == null) {
return;
}
try {
setName(info.getString("name"));
} catch (JSONException e) {
e.printStackTrace();
}
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/BleRequest.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
public class BleRequest {
public enum RequestType {
CONNECT_GATT, DISCOVER_SERVICE, CHARACTERISTIC_NOTIFICATION, CHARACTERISTIC_INDICATION, READ_CHARACTERISTIC, READ_DESCRIPTOR, READ_RSSI, WRITE_CHARACTERISTIC, WRITE_DESCRIPTOR, CHARACTERISTIC_STOP_NOTIFICATION
};
public enum FailReason {
START_FAILED, TIMEOUT, RESULT_FAILED
}
public RequestType type;
public String address;
public BleGattCharacteristic characteristic;
public String remark;
public BleRequest(RequestType type, String address) {
this.type = type;
this.address = address;
}
public BleRequest(RequestType type, String address,
BleGattCharacteristic characteristic) {
this.type = type;
this.address = address;
this.characteristic = characteristic;
}
public BleRequest(RequestType type, String address,
BleGattCharacteristic characteristic, String remark) {
this.type = type;
this.address = address;
this.characteristic = characteristic;
this.remark = remark;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof BleRequest)) {
return false;
}
BleRequest br = (BleRequest) o;
return (this.type == br.type && this.address.equals(br.address));
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/BleService.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import com.stag.bluetooth.extend.BleRequest.FailReason;
import com.stag.bluetooth.extend.BleRequest.RequestType;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.UUID;
public class BleService extends Service {
private static final String TAG = "blelib";
/** Intent for broadcast */
public static final String BLE_NOT_SUPPORTED = "com.xtremeprog.sdk.ble.not_supported";
public static final String BLE_NO_BT_ADAPTER = "com.xtremeprog.sdk.ble.no_bt_adapter";
public static final String BLE_STATUS_ABNORMAL = "com.xtremeprog.sdk.ble.status_abnormal";
/**
* @see BleService#bleRequestFailed
*/
public static final String BLE_REQUEST_FAILED = "com.xtremeprog.sdk.ble.request_failed";
/**
* @see BleService#bleDeviceFound
*/
public static final String BLE_DEVICE_FOUND = "com.xtremeprog.sdk.ble.device_found";
/**
* @see BleService#bleGattConnected
*/
public static final String BLE_GATT_CONNECTED = "com.xtremeprog.sdk.ble.gatt_connected";
/**
* @see BleService#bleGattDisConnected
*/
public static final String BLE_GATT_DISCONNECTED = "com.xtremeprog.sdk.ble.gatt_disconnected";
/**
* @see BleService#bleServiceDiscovered
*/
public static final String BLE_SERVICE_DISCOVERED = "com.xtremeprog.sdk.ble.service_discovered";
/**
* @see BleService#bleCharacteristicRead
*/
public static final String BLE_CHARACTERISTIC_READ = "com.xtremeprog.sdk.ble.characteristic_read";
/**
* @see BleService#bleCharacteristicNotification
*/
public static final String BLE_CHARACTERISTIC_NOTIFICATION = "com.xtremeprog.sdk.ble.characteristic_notification";
/**
* @see BleService#bleCharacteristicIndication
*/
public static final String BLE_CHARACTERISTIC_INDICATION = "com.xtremeprog.sdk.ble.characteristic_indication";
/**
* @see BleService#bleCharacteristicWrite
*/
public static final String BLE_CHARACTERISTIC_WRITE = "com.xtremeprog.sdk.ble.characteristic_write";
/**
* @see BleService#bleCharacteristicChanged
*/
public static final String BLE_CHARACTERISTIC_CHANGED = "com.xtremeprog.sdk.ble.characteristic_changed";
/** Intent extras */
public static final String EXTRA_DEVICE = "DEVICE";
public static final String EXTRA_RSSI = "RSSI";
public static final String EXTRA_SCAN_RECORD = "SCAN_RECORD";
public static final String EXTRA_SOURCE = "SOURCE";
public static final String EXTRA_ADDR = "ADDRESS";
public static final String EXTRA_CONNECTED = "CONNECTED";
public static final String EXTRA_STATUS = "STATUS";
public static final String EXTRA_UUID = "UUID";
public static final String EXTRA_VALUE = "VALUE";
public static final String EXTRA_REQUEST = "REQUEST";
public static final String EXTRA_REASON = "REASON";
/** Source of device entries in the device list */
public static final int DEVICE_SOURCE_SCAN = 0;
public static final int DEVICE_SOURCE_BONDED = 1;
public static final int DEVICE_SOURCE_CONNECTED = 2;
public static final UUID DESC_CCC = UUID
.fromString("00002902-0000-1000-8000-00805f9b34fb");
public enum BLESDK {
NOT_SUPPORTED, ANDROID, SAMSUNG, BROADCOM
}
private final IBinder mBinder = new LocalBinder();
private BLESDK mBleSDK;
private IBle mBle;
private Queue mRequestQueue = new LinkedList();
private BleRequest mCurrentRequest = null;
private static final int REQUEST_TIMEOUT = 10 * 10; // total timeout =
// REQUEST_TIMEOUT *
// 100ms
private boolean mCheckTimeout = false;
private int mElapsed = 0;
private Thread mRequestTimeout;
private String mNotificationAddress;
private Runnable mTimeoutRunnable = new Runnable() {
@Override
public void run() {
Log.d(TAG, "monitoring thread start");
mElapsed = 0;
try {
while (mCheckTimeout) {
// Log.d(TAG, "monitoring timeout seconds: " + mElapsed);
Thread.sleep(100);
mElapsed++;
if (mElapsed > REQUEST_TIMEOUT && mCurrentRequest != null) {
Log.d(TAG, "-processrequest type "
+ mCurrentRequest.type + " address "
+ mCurrentRequest.address + " [timeout]");
bleRequestFailed(mCurrentRequest.address,
mCurrentRequest.type, FailReason.TIMEOUT);
bleStatusAbnormal("-processrequest type "
+ mCurrentRequest.type + " address "
+ mCurrentRequest.address + " [timeout]");
if (mBle != null) {
mBle.disconnect(mCurrentRequest.address);
}
new Thread(new Runnable() {
@Override
public void run() {
mCurrentRequest = null;
processNextRequest();
}
}, "th-ble").start();
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Log.d(TAG, "monitoring thread exception");
}
Log.d(TAG, "monitoring thread stop");
}
};
public static IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BLE_NOT_SUPPORTED);
intentFilter.addAction(BLE_NO_BT_ADAPTER);
intentFilter.addAction(BLE_STATUS_ABNORMAL);
intentFilter.addAction(BLE_REQUEST_FAILED);
intentFilter.addAction(BLE_DEVICE_FOUND);
intentFilter.addAction(BLE_GATT_CONNECTED);
intentFilter.addAction(BLE_GATT_DISCONNECTED);
intentFilter.addAction(BLE_SERVICE_DISCOVERED);
intentFilter.addAction(BLE_CHARACTERISTIC_READ);
intentFilter.addAction(BLE_CHARACTERISTIC_NOTIFICATION);
intentFilter.addAction(BLE_CHARACTERISTIC_WRITE);
intentFilter.addAction(BLE_CHARACTERISTIC_CHANGED);
return intentFilter;
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
public BleService getService() {
return BleService.this;
}
}
@Override
public void onCreate() {
mBleSDK = getBleSDK();
if (mBleSDK == BLESDK.NOT_SUPPORTED) {
return;
}
Log.d(TAG, " " + mBleSDK);
if (mBleSDK == BLESDK.BROADCOM) {
mBle = new BroadcomBle(this);
} else if (mBleSDK == BLESDK.ANDROID) {
mBle = new AndroidBle(this);
} else if (mBleSDK == BLESDK.SAMSUNG) {
mBle = new SamsungBle(this);
}
}
protected void bleNotSupported() {
Intent intent = new Intent(BleService.BLE_NOT_SUPPORTED);
sendBroadcast(intent);
}
protected void bleNoBtAdapter() {
Intent intent = new Intent(BleService.BLE_NO_BT_ADAPTER);
sendBroadcast(intent);
}
private BLESDK getBleSDK() {
if (getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE)) {
// android 4.3
return BLESDK.ANDROID;
}
ArrayList libraries = new ArrayList();
for (String i : getPackageManager().getSystemSharedLibraryNames()) {
libraries.add(i);
}
if (android.os.Build.VERSION.SDK_INT >= 17) {
// android 4.2.2
if (libraries.contains("com.samsung.android.sdk.bt")) {
return BLESDK.SAMSUNG;
} else if (libraries.contains("com.broadcom.bt")) {
return BLESDK.BROADCOM;
}
}
bleNotSupported();
return BLESDK.NOT_SUPPORTED;
}
public IBle getBle() {
return mBle;
}
/**
* Send {@link BleService#BLE_DEVICE_FOUND} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_DEVICE} device {@link BluetoothDevice}
* {@link BleService#EXTRA_RSSI} rssi int
* {@link BleService#EXTRA_SCAN_RECORD} scan record byte[]
* {@link BleService#EXTRA_SOURCE} source int, not used now
*/
protected void bleDeviceFound(BluetoothDevice device, int rssi,
byte[] scanRecord, int source) {
Log.d("blelib", "[" + device.getName() + "] device found "
+ device.getAddress());
Intent intent = new Intent(BleService.BLE_DEVICE_FOUND);
intent.putExtra(BleService.EXTRA_DEVICE, device);
intent.putExtra(BleService.EXTRA_RSSI, rssi);
intent.putExtra(BleService.EXTRA_SCAN_RECORD, scanRecord);
intent.putExtra(BleService.EXTRA_SOURCE, source);
sendBroadcast(intent);
}
/**
* Send {@link BleService#BLE_GATT_CONNECTED} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_DEVICE} device {@link BluetoothDevice}
*/
protected void bleGattConnected(BluetoothDevice device) {
Intent intent = new Intent(BLE_GATT_CONNECTED);
intent.putExtra(EXTRA_DEVICE, device);
intent.putExtra(EXTRA_ADDR, device.getAddress());
sendBroadcast(intent);
requestProcessed(device.getAddress(), RequestType.CONNECT_GATT, true);
}
/**
* Send {@link BleService#BLE_GATT_DISCONNECTED} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
*
* @param address
*/
protected void bleGattDisConnected(String address) {
Intent intent = new Intent(BLE_GATT_DISCONNECTED);
intent.putExtra(EXTRA_ADDR, address);
sendBroadcast(intent);
requestProcessed(address, RequestType.CONNECT_GATT, false);
}
/**
* Send {@link BleService#BLE_SERVICE_DISCOVERED} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
*
* @param address
*/
protected void bleServiceDiscovered(String address) {
Intent intent = new Intent(BLE_SERVICE_DISCOVERED);
intent.putExtra(EXTRA_ADDR, address);
sendBroadcast(intent);
requestProcessed(address, RequestType.DISCOVER_SERVICE, true);
}
protected void requestProcessed(String address, RequestType requestType,
boolean success) {
if (mCurrentRequest != null && mCurrentRequest.type == requestType) {
clearTimeoutThread();
Log.d(TAG, "-processrequest type " + requestType + " address "
+ address + " [success: " + success + "]");
if (!success) {
bleRequestFailed(mCurrentRequest.address, mCurrentRequest.type,
FailReason.RESULT_FAILED);
}
new Thread(new Runnable() {
@Override
public void run() {
mCurrentRequest = null;
processNextRequest();
}
}, "th-ble").start();
}
}
private void clearTimeoutThread() {
if (mRequestTimeout.isAlive()) {
try {
mCheckTimeout = false;
mRequestTimeout.join();
mRequestTimeout = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Send {@link BleService#BLE_CHARACTERISTIC_READ} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_UUID} characteristic uuid {@link String}
* {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now
* {@link BleService#EXTRA_VALUE} data byte[]
*
* @param address
* @param uuid
* @param status
* @param value
*/
protected void bleCharacteristicRead(String address, String uuid,
int status, byte[] value) {
Intent intent = new Intent(BLE_CHARACTERISTIC_READ);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_UUID, uuid);
intent.putExtra(EXTRA_STATUS, status);
intent.putExtra(EXTRA_VALUE, value);
sendBroadcast(intent);
requestProcessed(address, RequestType.READ_CHARACTERISTIC, true);
}
protected void addBleRequest(BleRequest request) {
synchronized (mRequestQueue) {
mRequestQueue.add(request);
processNextRequest();
}
}
private synchronized void processNextRequest() {
if (mCurrentRequest != null) {
return;
}
if (mRequestQueue.isEmpty()) {
return;
}
mCurrentRequest = mRequestQueue.remove();
Log.d(TAG, "+processrequest type " + mCurrentRequest.type + " address "
+ mCurrentRequest.address + " remark " + mCurrentRequest.remark);
startTimeoutThread();
boolean ret = false;
switch (mCurrentRequest.type) {
case CONNECT_GATT:
ret = ((IBleRequestHandler) mBle).connect(mCurrentRequest.address);
break;
case DISCOVER_SERVICE:
ret = mBle.discoverServices(mCurrentRequest.address);
break;
case CHARACTERISTIC_NOTIFICATION:
case CHARACTERISTIC_INDICATION:
case CHARACTERISTIC_STOP_NOTIFICATION:
ret = ((IBleRequestHandler) mBle).characteristicNotification(
mCurrentRequest.address, mCurrentRequest.characteristic);
break;
case READ_CHARACTERISTIC:
ret = ((IBleRequestHandler) mBle).readCharacteristic(
mCurrentRequest.address, mCurrentRequest.characteristic);
break;
case WRITE_CHARACTERISTIC:
ret = ((IBleRequestHandler) mBle).writeCharacteristic(
mCurrentRequest.address, mCurrentRequest.characteristic);
break;
case READ_DESCRIPTOR:
break;
default:
break;
}
if (!ret) {
clearTimeoutThread();
Log.d(TAG, "-processrequest type " + mCurrentRequest.type
+ " address " + mCurrentRequest.address + " [fail start]");
bleRequestFailed(mCurrentRequest.address, mCurrentRequest.type,
FailReason.START_FAILED);
new Thread(new Runnable() {
@Override
public void run() {
mCurrentRequest = null;
processNextRequest();
}
}, "th-ble").start();
}
}
private void startTimeoutThread() {
mCheckTimeout = true;
mRequestTimeout = new Thread(mTimeoutRunnable);
mRequestTimeout.start();
}
protected BleRequest getCurrentRequest() {
return mCurrentRequest;
}
protected void setCurrentRequest(BleRequest mCurrentRequest) {
this.mCurrentRequest = mCurrentRequest;
}
/**
* Send {@link BleService#BLE_CHARACTERISTIC_NOTIFICATION} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_UUID} characteristic uuid {@link String}
* {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now
*
* @param address
* @param uuid
* @param status
*/
protected void bleCharacteristicNotification(String address, String uuid,
boolean isEnabled, int status) {
Intent intent = new Intent(BLE_CHARACTERISTIC_NOTIFICATION);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_UUID, uuid);
intent.putExtra(EXTRA_VALUE, isEnabled);
intent.putExtra(EXTRA_STATUS, status);
sendBroadcast(intent);
if (isEnabled) {
requestProcessed(address, RequestType.CHARACTERISTIC_NOTIFICATION,
true);
} else {
requestProcessed(address,
RequestType.CHARACTERISTIC_STOP_NOTIFICATION, true);
}
setNotificationAddress(address);
}
/**
* Send {@link BleService#BLE_CHARACTERISTIC_INDICATION} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_UUID} characteristic uuid {@link String}
* {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now
*
* @param address
* @param uuid
* @param status
*/
protected void bleCharacteristicIndication(String address, String uuid,
int status) {
Intent intent = new Intent(BLE_CHARACTERISTIC_INDICATION);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_UUID, uuid);
intent.putExtra(EXTRA_STATUS, status);
sendBroadcast(intent);
requestProcessed(address, RequestType.CHARACTERISTIC_INDICATION, true);
setNotificationAddress(address);
}
/**
* Send {@link BleService#BLE_CHARACTERISTIC_WRITE} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_UUID} characteristic uuid {@link String}
* {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now
*
* @param address
* @param uuid
* @param status
*/
protected void bleCharacteristicWrite(String address, String uuid,
int status) {
Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_UUID, uuid);
intent.putExtra(EXTRA_STATUS, status);
sendBroadcast(intent);
requestProcessed(address, RequestType.WRITE_CHARACTERISTIC, true);
}
/**
* Send {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_UUID} characteristic uuid {@link String}
* {@link BleService#EXTRA_VALUE} data byte[]
*
* @param address
* @param uuid
* @param value
*/
protected void bleCharacteristicChanged(String address, String uuid,
byte[] value) {
Intent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_UUID, uuid);
intent.putExtra(EXTRA_VALUE, value);
sendBroadcast(intent);
}
/**
* @param reason
*/
protected void bleStatusAbnormal(String reason) {
Intent intent = new Intent(BLE_STATUS_ABNORMAL);
intent.putExtra(EXTRA_VALUE, reason);
sendBroadcast(intent);
}
/**
* Sent when BLE request failed.
*
* Data in the broadcast intent:
* {@link BleService#EXTRA_ADDR} device address {@link String}
* {@link BleService#EXTRA_REQUEST} request type
* {@link BleRequest.RequestType}
* {@link BleService#EXTRA_REASON} fail reason {@link BleRequest.FailReason}
*/
protected void bleRequestFailed(String address, RequestType type,
FailReason reason) {
Intent intent = new Intent(BLE_REQUEST_FAILED);
intent.putExtra(EXTRA_ADDR, address);
intent.putExtra(EXTRA_REQUEST, type);
intent.putExtra(EXTRA_REASON, reason.ordinal());
sendBroadcast(intent);
}
protected String getNotificationAddress() {
return mNotificationAddress;
}
protected void setNotificationAddress(String mNotificationAddress) {
this.mNotificationAddress = mNotificationAddress;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/BroadcomBle.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import com.broadcom.bt.gatt.BluetoothGatt;
import com.broadcom.bt.gatt.BluetoothGattAdapter;
import com.broadcom.bt.gatt.BluetoothGattCallback;
import com.broadcom.bt.gatt.BluetoothGattCharacteristic;
import com.broadcom.bt.gatt.BluetoothGattDescriptor;
import com.broadcom.bt.gatt.BluetoothGattService;
import com.stag.bluetooth.extend.BleRequest.RequestType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class BroadcomBle implements IBle, IBleRequestHandler {
private BluetoothAdapter mBtAdapter;
private BleService mService;
private BluetoothGatt mBluetoothGatt;
private boolean mScanning;
private String mAddress;
private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
@Override
public void onAppRegistered(int status) {
}
@Override
public void onScanResult(BluetoothDevice device, int rssi,
byte[] scanRecord) {
mService.bleDeviceFound(device, rssi, scanRecord,
BleService.DEVICE_SOURCE_SCAN);
}
@Override
public void onConnectionStateChange(BluetoothDevice device, int status,
int newState) {
if (mBluetoothGatt == null) {
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
mService.bleGattConnected(device);
mBluetoothGatt.discoverServices(device);
mAddress = device.getAddress();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mService.bleGattDisConnected(device.getAddress());
mAddress = null;
}
}
@Override
public void onServicesDiscovered(BluetoothDevice device, int status) {
mService.bleServiceDiscovered(device.getAddress());
}
@Override
public void onCharacteristicRead(
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
mService.bleCharacteristicRead(mAddress, characteristic
.getUuid().toString(), status, characteristic
.getValue());
}
}
@Override
public void onCharacteristicChanged(
BluetoothGattCharacteristic characteristic) {
String address = mService.getNotificationAddress();
mService.bleCharacteristicChanged(address, characteristic.getUuid()
.toString(), characteristic.getValue());
}
@Override
public void onDescriptorRead(BluetoothGattDescriptor descriptor,
int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
byte[] value = descriptor.getValue();
byte[] val_set = null;
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
val_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
val_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
val_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
if (Arrays.equals(value, val_set)) {
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), true,
status);
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
mService.bleCharacteristicIndication(address, descriptor
.getCharacteristic().getUuid().toString(), status);
} else {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), false,
status);
}
return;
}
if (!descriptor.setValue(val_set)) {
mService.requestProcessed(address, request.type, false);
}
mBluetoothGatt.writeDescriptor(descriptor);
};
@Override
public void onDescriptorWrite(BluetoothGattDescriptor descriptor,
int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION
|| request.type == RequestType.CHARACTERISTIC_INDICATION
|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address, request.type, false);
return;
}
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), true,
status);
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
mService.bleCharacteristicIndication(address, descriptor
.getCharacteristic().getUuid().toString(), status);
} else {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), false,
status);
}
return;
}
};
};
private final BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
mBluetoothGatt = (BluetoothGatt) proxy;
mBluetoothGatt.registerApp(mGattCallbacks);
}
@Override
public void onServiceDisconnected(int profile) {
for ( BluetoothDevice d : mBluetoothGatt.getConnectedDevices() ) {
mBluetoothGatt.cancelConnection(d);
}
mBluetoothGatt = null;
}
};
public BroadcomBle(BleService service) {
mService = service;
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBtAdapter == null) {
mService.bleNoBtAdapter();
return;
}
BluetoothGattAdapter.getProfileProxy(mService, mProfileServiceListener,
BluetoothGattAdapter.GATT);
}
@Override
public void startScan() {
if (mScanning) {
return;
}
if (mBluetoothGatt == null) {
mScanning = false;
return;
}
mScanning = true;
mBluetoothGatt.startScan();
}
@Override
public void stopScan() {
if (!mScanning || mBluetoothGatt == null) {
return;
}
mScanning = false;
mBluetoothGatt.stopScan();
}
@Override
public boolean adapterEnabled() {
if (mBtAdapter != null) {
return mBtAdapter.isEnabled();
}
return false;
}
@Override
public boolean connect(String address) {
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
return mBluetoothGatt.connect(device, false);
}
@Override
public void disconnect(String address) {
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
mBluetoothGatt.cancelConnection(device);
}
@Override
public ArrayList getServices(String address) {
ArrayList list = new ArrayList();
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
List services = mBluetoothGatt
.getServices(device);
for (BluetoothGattService s : services) {
list.add(new BleGattService(s));
}
return list;
}
@Override
public boolean requestReadCharacteristic(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,
address, characteristic));
return true;
}
@Override
public boolean discoverServices(String address) {
return true;
}
@Override
public boolean readCharacteristic(String address,
BleGattCharacteristic characteristic) {
if (characteristic.getGattCharacteristicB() != null) {
return mBluetoothGatt.readCharacteristic(characteristic
.getGattCharacteristicB());
}
return false;
}
@Override
public BleGattService getService(String address, UUID uuid) {
BluetoothGattService service = mBluetoothGatt.getService(
mBtAdapter.getRemoteDevice(address), uuid);
if (service == null) {
return null;
} else {
return new BleGattService(service);
}
}
@Override
public boolean requestCharacteristicNotification(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_NOTIFICATION, address,
characteristic));
return true;
}
@Override
public boolean characteristicNotification(String address,
BleGattCharacteristic characteristic) {
BleRequest request = mService.getCurrentRequest();
BluetoothGattCharacteristic b = characteristic.getGattCharacteristicB();
boolean enable = true;
if (request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {
enable = false;
}
if (!mBluetoothGatt.setCharacteristicNotification(b, enable)) {
return false;
}
BluetoothGattDescriptor descriptor = b
.getDescriptor(BleService.DESC_CCC);
if (descriptor == null) {
return false;
}
return mBluetoothGatt.readDescriptor(descriptor);
}
@Override
public boolean requestWriteCharacteristic(String address,
BleGattCharacteristic characteristic, String remark) {
mService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,
address, characteristic));
return true;
}
@Override
public boolean writeCharacteristic(String address,
BleGattCharacteristic characteristic) {
return mBluetoothGatt.writeCharacteristic(characteristic
.getGattCharacteristicB());
}
@Override
public boolean requestConnect(String address) {
if (mAddress != null) {
return false;
}
mService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));
return true;
}
@Override
public String getBTAdapterMacAddr() {
if (mBtAdapter != null) {
return mBtAdapter.getAddress();
}
return null;
}
@Override
public boolean requestIndication(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_INDICATION, address, characteristic));
return true;
}
@Override
public boolean requestStopNotification(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_STOP_NOTIFICATION, address,
characteristic));
return true;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/IBle.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import java.util.ArrayList;
import java.util.UUID;
public interface IBle {
public String getBTAdapterMacAddr();
/**
* Will receive broadcast {@link BleService#BLE_DEVICE_FOUND} if device
* found.
*/
public void startScan();
/**
* Stop BLE scan.
*/
public void stopScan();
/**
* Check if bluetooth adapter is enabled.
*
* @return enabled
*/
public boolean adapterEnabled();
/**
* Disconnect BLE device. Will receive
* {@link BleService#BLE_GATT_DISCONNECTED} broadcast if device
* disconnected.
*
* @param address
* BLE device address.
*/
public void disconnect(String address);
/**
* Discover BLE services. Will receive
* {@link BleService#BLE_SERVICE_DISCOVERED} broadcast if device service
* discovered.
*
* @param address
* @return
*/
public boolean discoverServices(String address);
/**
* Get discovered services for BLE device. Call this function after
* {@link BleService#BLE_SERVICE_DISCOVERED} broadcast is received.
*
* @param address
* @return List of {@link BleGattService}
*/
public ArrayList getServices(String address);
/**
* Get discovered service by uuid. Call this function after
* {@link BleService#BLE_SERVICE_DISCOVERED} broadcast is received.
*
* @param address
* @param uuid
* @return {@link BleGattService}
*/
public BleGattService getService(String address, UUID uuid);
/**
* Request to connect a BLE device by address. Will receive
* {@link BleService#BLE_GATT_CONNECTED} broadcast if device connected.
*
* @param address
* @return if request be inserted into queue successfully.
*/
public boolean requestConnect(String address);
/**
* Request to read characteristic. Will receive
* {@link BleService#BLE_CHARACTERISTIC_READ} broadcast if characteristic
* read.
*
* @param address
* @param characteristic
* Get characteristic from {@link BleGattService}
* @return if request be inserted into queue successfully.
*/
public boolean requestReadCharacteristic(String address,
BleGattCharacteristic characteristic);
/**
* Request characteristic notification. Will receive
* {@link BleService#BLE_CHARACTERISTIC_NOTIFICATION} broadcast if
* notification set OK. When the characteristic's value changed,
* {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast will be received
* also.
*
* @param address
* @param characteristic
* Get characteristic from {@link BleGattService}
* @return if request be inserted into queue successfully.
*/
public boolean requestCharacteristicNotification(String address,
BleGattCharacteristic characteristic);
public boolean requestStopNotification(String address,
BleGattCharacteristic characteristic);
/**
* Request characteristic indication. Will receive
* {@link BleService#BLE_CHARACTERISTIC_INDICATION} broadcast if indication
* set OK. When the characteristic's value changed,
* {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast will be received
* also.
*
* @param address
* @param characteristic
* Get characteristic from {@link BleGattService}
* @return if request be inserted into queue successfully.
*/
public boolean requestIndication(String address,
BleGattCharacteristic characteristic);
/**
* Request write characteristic value. Will receive
* {@link BleService#BLE_CHARACTERISTIC_WRITE} broadcast if characteristic
* value be written.
*
* @param address
* @param characteristic
* Get characteristic from {@link BleGattService}
* @param remark
* For debug purpose.
* @return if request be inserted into queue successfully.
*/
public boolean requestWriteCharacteristic(String address,
BleGattCharacteristic characteristic, String remark);
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/IBleRequestHandler.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
public interface IBleRequestHandler {
public boolean connect(String address);
/**
* @param address
* @param characteristic
* @return
*/
public boolean readCharacteristic(String address,
BleGattCharacteristic characteristic);
/**
* @param address
* @param characteristic
* @return
*/
public boolean characteristicNotification(String address,
BleGattCharacteristic characteristic);
/**
* @param address
* @param characteristic
* @return
*/
public boolean writeCharacteristic(String address,
BleGattCharacteristic characteristic);
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/extend/SamsungBle.java
================================================
/**
* This XPG software is supplied to you by Xtreme Programming Group, Inc.
* ("XPG") in consideration of your agreement to the following terms, and your
* use, installation, modification or redistribution of this XPG software
* constitutes acceptance of these terms.� If you do not agree with these terms,
* please do not use, install, modify or redistribute this XPG software.
*
* In consideration of your agreement to abide by the following terms, and
* subject to these terms, XPG grants you a non-exclusive license, under XPG's
* copyrights in this original XPG software (the "XPG Software"), to use and
* redistribute the XPG Software, in source and/or binary forms; provided that
* if you redistribute the XPG Software, with or without modifications, you must
* retain this notice and the following text and disclaimers in all such
* redistributions of the XPG Software. Neither the name, trademarks, service
* marks or logos of XPG Inc. may be used to endorse or promote products derived
* from the XPG Software without specific prior written permission from XPG.�
* Except as expressly stated in this notice, no other rights or licenses,
* express or implied, are granted by XPG herein, including but not limited to
* any patent rights that may be infringed by your derivative works or by other
* works in which the XPG Software may be incorporated.
*
* The XPG Software is provided by XPG on an "AS IS" basis.� XPG MAKES NO
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
* COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
* AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER
* THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
* OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)
* is a digital solutions company based in the United States and China. XPG
* integrates cutting-edge hardware designs, mobile applications, and cloud
* computing technologies to bring innovative products to the marketplace. XPG's
* partners and customers include global leading corporations in semiconductor,
* home appliances, health/wellness electronics, toys and games, and automotive
* industries. Visit www.xtremeprog.com for more information.
*
* Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.
*/
package com.stag.bluetooth.extend;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
import com.samsung.android.sdk.bt.gatt.BluetoothGatt;
import com.samsung.android.sdk.bt.gatt.BluetoothGattAdapter;
import com.samsung.android.sdk.bt.gatt.BluetoothGattCallback;
import com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic;
import com.samsung.android.sdk.bt.gatt.BluetoothGattDescriptor;
import com.samsung.android.sdk.bt.gatt.BluetoothGattService;
import com.stag.bluetooth.extend.BleRequest.RequestType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class SamsungBle implements IBle, IBleRequestHandler {
protected static final String TAG = "blelib";
private BluetoothAdapter mBtAdapter;
private BleService mService;
private BluetoothGatt mBluetoothGatt;
private boolean mScanning;
private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
@Override
public void onAppRegistered(int status) {
}
@Override
public void onScanResult(BluetoothDevice device, int rssi,
byte[] scanRecord) {
mService.bleDeviceFound(device, rssi, scanRecord,
BleService.DEVICE_SOURCE_SCAN);
}
@Override
public void onConnectionStateChange(BluetoothDevice device, int status,
int newState) {
if (mBluetoothGatt == null) {
return;
}
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect(device.getAddress());
mService.bleGattDisConnected(device.getAddress());
return;
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
mService.bleGattConnected(device);
mService.addBleRequest(new BleRequest(
RequestType.DISCOVER_SERVICE, device.getAddress()));
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mService.bleGattDisConnected(device.getAddress());
disconnect(device.getAddress());
}
}
@Override
public void onServicesDiscovered(BluetoothDevice device, int status) {
String address = device.getAddress();
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect(address);
mService.bleGattDisConnected(address);
mService.requestProcessed(address,
RequestType.DISCOVER_SERVICE, false);
return;
}
mService.bleServiceDiscovered(device.getAddress());
}
@Override
public void onCharacteristicRead(
BluetoothGattCharacteristic characteristic, int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.READ_CHARACTERISTIC, false);
return;
}
mService.bleCharacteristicRead(address, characteristic.getUuid()
.toString(), status, characteristic.getValue());
}
@Override
public void onCharacteristicChanged(
BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged");
String address = mService.getNotificationAddress();
mService.bleCharacteristicChanged(address, characteristic.getUuid()
.toString(), characteristic.getValue());
}
@Override
public void onCharacteristicWrite(
BluetoothGattCharacteristic characteristic, int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address,
RequestType.WRITE_CHARACTERISTIC, false);
return;
}
mService.bleCharacteristicWrite(address, characteristic.getUuid()
.toString(), status);
};
@Override
public void onDescriptorRead(BluetoothGattDescriptor descriptor,
int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
byte[] value = descriptor.getValue();
byte[] val_set = null;
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
val_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
val_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
val_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
if (Arrays.equals(value, val_set)) {
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), true,
status);
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
mService.bleCharacteristicIndication(address, descriptor
.getCharacteristic().getUuid().toString(), status);
} else {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), false,
status);
}
return;
}
if (!descriptor.setValue(val_set)) {
mService.requestProcessed(address, request.type, false);
}
mBluetoothGatt.writeDescriptor(descriptor);
};
@Override
public void onDescriptorWrite(BluetoothGattDescriptor descriptor,
int status) {
BleRequest request = mService.getCurrentRequest();
String address = request.address;
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION
|| request.type == RequestType.CHARACTERISTIC_INDICATION
|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {
if (status != BluetoothGatt.GATT_SUCCESS) {
mService.requestProcessed(address, request.type, false);
return;
}
if (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), true,
status);
} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {
mService.bleCharacteristicIndication(address, descriptor
.getCharacteristic().getUuid().toString(), status);
} else {
mService.bleCharacteristicNotification(address, descriptor
.getCharacteristic().getUuid().toString(), false,
status);
}
return;
}
};
};
private final BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
mBluetoothGatt = (BluetoothGatt) proxy;
mBluetoothGatt.registerApp(mGattCallbacks);
}
@Override
public void onServiceDisconnected(int profile) {
mBluetoothGatt = null;
}
};
public SamsungBle(BleService service) {
mService = service;
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBtAdapter == null) {
mService.bleNoBtAdapter();
return;
}
BluetoothGattAdapter.getProfileProxy(mService, mProfileServiceListener,
BluetoothGattAdapter.GATT);
}
@Override
public void startScan() {
if (mScanning) {
return;
}
if (mBluetoothGatt == null) {
mScanning = false;
return;
}
mScanning = true;
mBluetoothGatt.startScan();
}
@Override
public void stopScan() {
if (!mScanning || mBluetoothGatt == null) {
return;
}
mScanning = false;
mBluetoothGatt.stopScan();
}
@Override
public boolean adapterEnabled() {
if (mBtAdapter != null) {
return mBtAdapter.isEnabled();
}
return false;
}
@Override
public boolean connect(String address) {
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
return mBluetoothGatt.connect(device, false);
}
@Override
public void disconnect(String address) {
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
mBluetoothGatt.cancelConnection(device);
}
@Override
public ArrayList getServices(String address) {
ArrayList list = new ArrayList();
BluetoothDevice device = mBtAdapter.getRemoteDevice(address);
List services = mBluetoothGatt
.getServices(device);
for (BluetoothGattService s : services) {
list.add(new BleGattService(s));
}
return list;
}
@Override
public boolean requestReadCharacteristic(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,
address, characteristic));
return true;
}
@Override
public boolean discoverServices(String address) {
return mBluetoothGatt.discoverServices(mBtAdapter
.getRemoteDevice(address));
}
@Override
public boolean readCharacteristic(String address,
BleGattCharacteristic characteristic) {
return mBluetoothGatt.readCharacteristic(characteristic
.getGattCharacteristicS());
}
@Override
public BleGattService getService(String address, UUID uuid) {
BluetoothGattService service = mBluetoothGatt.getService(
mBtAdapter.getRemoteDevice(address), uuid);
if (service == null) {
return null;
} else {
return new BleGattService(service);
}
}
@Override
public boolean requestCharacteristicNotification(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_NOTIFICATION, address,
characteristic));
return true;
}
@Override
public boolean characteristicNotification(String address,
BleGattCharacteristic characteristic) {
BluetoothGattCharacteristic c = characteristic.getGattCharacteristicS();
if (!mBluetoothGatt.setCharacteristicNotification(c, true)) {
return false;
}
BluetoothGattDescriptor descriptor = c
.getDescriptor(BleService.DESC_CCC);
if (descriptor == null) {
return false;
}
return mBluetoothGatt.readDescriptor(descriptor);
}
@Override
public boolean requestWriteCharacteristic(String address,
BleGattCharacteristic characteristic, String remark) {
mService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,
address, characteristic));
return true;
}
@Override
public boolean writeCharacteristic(String address,
BleGattCharacteristic characteristic) {
return mBluetoothGatt.writeCharacteristic(characteristic
.getGattCharacteristicS());
}
@Override
public boolean requestConnect(String address) {
mService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));
return true;
}
@Override
public String getBTAdapterMacAddr() {
if (mBtAdapter != null) {
return mBtAdapter.getAddress();
}
return null;
}
@Override
public boolean requestIndication(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_INDICATION, address, characteristic));
return true;
}
@Override
public boolean requestStopNotification(String address,
BleGattCharacteristic characteristic) {
mService.addBleRequest(new BleRequest(
RequestType.CHARACTERISTIC_STOP_NOTIFICATION, address,
characteristic));
return true;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/helper/BleHelper.java
================================================
package com.stag.bluetooth.helper;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.IBinder;
import com.stag.bluetooth.extend.BleService;
import com.stag.bluetooth.extend.IBle;
import com.stag.bluetooth.protocol.Protocol;
import com.stag.bluetooth.util.ByteUtils;
import com.stag.bluetooth.util.Logs;
import java.util.UUID;
/**
* Created by Administrator on 2016/11/14.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public final class BleHelper extends BluetoothHelper {
private static final String TAG = "LPQ";
public final static UUID DEFAULT_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public final static String BLE_SCAN_FINISH = "BLE_SCAN_FINISH";
private final static int SCAN_DURATION = 10000;
private final static short MAX_WRITE_DESCRIPTOR_TIME = 3000;
private final static int MAX_SINGLE_SZIE = 512;
private final static int DEFAULT_SINGLE_SZIE = 20;
private static BleHelper instance;
private BluetoothGatt mGatt;
private BluetoothGattCharacteristic mCRead;
private BluetoothGattCharacteristic mCWrite;
private Thread timeoutCountThread; //超时计数
private boolean isResumeTimeoutCount, isResumeScanCount;
private ServiceConnection connBle;
private BleService mBleService;
private IBle mBle;
private Thread scanCountThread; //扫描计时
private int mSingleSize = DEFAULT_SINGLE_SZIE;
private boolean mSetHeightSpeedModeResult;
private boolean mWriteDescriptorResult;
private boolean sendResult;//是否发生成功
public static BleHelper getInstance(Context context) {
if (instance == null) {
synchronized (BleHelper.class) {
instance = new BleHelper(context);
}
}
return instance;
}
private BleHelper(Context context) {
super(context);
}
@Override
public void startScan() {
if (mBle == null) {
bindBleService();
} else {
mBle.startScan();
}
}
@Override
public void stopScan() {
if (mBle != null) {
mBle.stopScan();
}
stopScanCount();
}
@Override
public void connect(final String address) {
mMaxTime = 0;
configHandler();
// BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
// BluetoothAdapter adapter = bluetoothManager.getAdapter();
setConnecting(true);
mDevice = mBluetoothAdapter.getRemoteDevice(address);
mCWrite = null;
mCRead = null;
mSingleSize = DEFAULT_SINGLE_SZIE;
mGatt = mDevice.connectGatt(mContext, false, mCallBack);
if (mGatt == null) {
Logs.d(TAG, "GATT NULL,FAIL");
handleConnectEvent(isConnecting(), false);
} else {
Logs.d(TAG, "start connect... " + address);
startTimeoutCount();
}
}
@Override
public void disconnect() {
handleConnectEvent(false, false);
}
private void bindBleService() {
connBle = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBleService = ((BleService.LocalBinder) service).getService();
mBle = mBleService.getBle();
if (mBle != null && mBle.adapterEnabled()) {
mBle.startScan();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBleService = null;
mBle = null;
}
};
Intent intent = new Intent(mContext, BleService.class);
mContext.bindService(intent, connBle, Context.BIND_AUTO_CREATE);
}
/**
* 开始扫描计时
*/
private void startScanCount() {
isResumeScanCount = true;
scanCountThread = new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(SCAN_DURATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isResumeScanCount)
stopScan();
}
};
scanCountThread.start();
}
/**
* 停止扫描计时
*/
private void stopScanCount() {
if (scanCountThread != null) {
isResumeScanCount = false;
scanCountThread.interrupt();
scanCountThread = null;
}
mContext.sendBroadcast(new Intent(BLE_SCAN_FINISH));
}
/**
* 开始连接超时计数
*/
private void startTimeoutCount() {
isResumeTimeoutCount = true;
timeoutCountThread = new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(CONNECT_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
Logs.d(TAG, "timeout:" + !isConnected + " isResumeTimeoutCount:" + isResumeTimeoutCount);
if (!isConnected && isResumeTimeoutCount)
handleConnectEvent(isConnecting(), false);
}
};
timeoutCountThread.start();
}
/**
* 停止连接超时计数
*/
private void stopTimeoutCount() {
if (timeoutCountThread != null) {
isResumeTimeoutCount = false;
timeoutCountThread.interrupt();
timeoutCountThread = null;
}
}
@Override
public void send(byte[] data) {
super.send(data);
if(mGatt==null || mCWrite==null || data==null || data.length==0)
return;
/*int c = data.length/20;
int remain = data.length%20;
int count = remain==0?c:c+1;
for (int i=0;imMaxTime){
mMaxTime = t;
Logs.d("LPQ","max interval:"+mMaxTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
byte[] sendData = null;
for (int i = 0; i <= data.length / mSingleSize; i++) {
if (i == data.length / mSingleSize) {
sendData = new byte[data.length % mSingleSize];
if (sendData.length == 0)
return;
for (int k = 0; k < data.length % mSingleSize; k++) {
sendData[k] = data[i * mSingleSize + k];
}
} else {
sendData = new byte[mSingleSize];
for (int k = 0; k < mSingleSize; k++) {
sendData[k] = data[i * mSingleSize + k];
}
}
if (!isConnected())
return;
mCWrite.setValue(sendData);
Logs.d("LPQ", "prepare big send");
sendResult = false;
boolean res = mGatt.writeCharacteristic(mCWrite);
Logs.d("LPQ", "after big writeCharacteristic:" + sendData.length + " result:"+res+" "+ByteUtils.toString(sendData));
if (sendResult) {
Logs.d("LPQ", "big success");
continue;
};
synchronized (BleHelper.this) {
try {
long i2 = System.currentTimeMillis();
wait(sendInterval);
long t = System.currentTimeMillis()-i2;
if (t>mMaxTime){
mMaxTime = t;
Logs.d("LPQ","max interval:"+mMaxTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
long mMaxTime;
private BluetoothGattCallback mCallBack = (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) ? null : new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
recv(characteristic.getValue());
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Logs.d(TAG, "success,discoverServices");
mGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Logs.d(TAG, "fail,stop");
stopTimeoutCount();
handleConnectEvent(isConnecting, false);
}
Logs.d(TAG, "connect state---" + newState);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
stopTimeoutCount();
if (status == BluetoothGatt.GATT_SUCCESS && mProtocol != null) {
if (mProtocol.getServiceUUID() != null) {
BluetoothGattService service = mGatt.getService(mProtocol.getServiceUUID());
if (service != null) {
mCWrite = service.getCharacteristic(mProtocol.getSendTunnelUUID());
mCRead = service.getCharacteristic(mProtocol.getRecvTunnelUUID());
}else {
Logs.d("LPQ", "service is null");
}
}
if (mCRead == null || mCWrite == null){
for (BluetoothGattService s : mGatt.getServices()) {
if(mCWrite == null){
mCWrite = s.getCharacteristic(mProtocol.getSendTunnelUUID());
if (mCWrite!=null)
Logs.d("LPQ", "write service:"+s.getUuid().toString());
}
if (mCRead == null) {
mCRead = s.getCharacteristic(mProtocol.getRecvTunnelUUID());
if (mCRead!=null)
Logs.d("LPQ", "read service:"+s.getUuid().toString());
}
if (mCWrite!=null&&mCRead!=null){
break;
}
}
}
if (mCRead != null && mCWrite != null) {
// if (mGatt.setCharacteristicNotification(mCRead, true)) {
/*BluetoothGattDescriptor descriptor = mCRead.getDescriptor(mProtocol.getDescriptorUUID() == null ? DEFAULT_DESCRIPTOR_UUID : mProtocol.getDescriptorUUID());
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}*/
/*List descriptors = mCRead.getDescriptors();
for(BluetoothGattDescriptor dp:descriptors){
dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(dp);
}*/
// }
boolean isSet = false;
byte[] value = null;
if (0 != (mCRead.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // 查看是否带有可通知属性notify
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
isSet = true;
} else if (0 != (mCRead.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) { // 查看是否带有indecation属性
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
isSet = true;
}
if (isSet){
UUID descriptorUUID = mProtocol.getDescriptorUUID() == null ? DEFAULT_DESCRIPTOR_UUID : mProtocol.getDescriptorUUID();
mGatt.setCharacteristicNotification(mCRead, true);
BluetoothGattDescriptor descriptor = mCRead.getDescriptor(descriptorUUID);
descriptor.setValue(value);
final boolean b = mGatt.writeDescriptor(descriptor);
//此处必须开线程,因为onDescriptorWrite是顺序回调执行的
new Thread(new Runnable() {
@Override
public void run() {
Logs.d(TAG, "writeDescriptor "+b);
synchronized (mGatt){
try {
mWriteDescriptorResult = false;
mGatt.wait(MAX_WRITE_DESCRIPTOR_TIME);
Logs.d(TAG, "write --- connect success" + isConnecting());
handleConnectEvent(isConnecting(), true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}else {
Logs.d(TAG, "no write --- connect success " + isConnecting());
handleConnectEvent(isConnecting(), true);
}
return;
} else {
Logs.d(TAG, "read-write null");
}
} else {
Logs.d(TAG, "onServicesDiscovered fail " + status);
}
handleConnectEvent(isConnecting(), false);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
//TODO:这个逻辑有点小问题
if (status == BluetoothGatt.GATT_SUCCESS) {
mSingleSize = mtu - 3;
mSetHeightSpeedModeResult = true;
Logs.d("LPQ", "MTU change success "+mtu);
}else {
mSetHeightSpeedModeResult = false;
Logs.d("LPQ", "MTU change fail "+mtu);
}
synchronized (mGatt){
mGatt.notify();
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
synchronized (BleHelper.this){
if (status==BluetoothGatt.GATT_SUCCESS){
sendResult = true;
Logs.d("LPQ", "send success "+ByteUtils.toString(characteristic.getValue()));
}else {
sendResult = false;
Logs.d("LPQ", "sen fail,status:"+status+" "+ByteUtils.toString(characteristic.getValue()));
}
BleHelper.this.notify();
Logs.d("LPQ", "send complete, notify end");
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
mWriteDescriptorResult = status==BluetoothGatt.GATT_SUCCESS;
Logs.d("LPQ", "Write result: "+mWriteDescriptorResult);
synchronized (mGatt){
mGatt.notify();
}
}
};
public boolean isHighSpeedMode(){
return mSingleSize > DEFAULT_SINGLE_SZIE;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean setHighConnectionPriority(boolean flag){
if (mGatt == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return false;
return mGatt.requestConnectionPriority(flag ? BluetoothGatt.CONNECTION_PRIORITY_HIGH:BluetoothGatt.CONNECTION_PRIORITY_BALANCED);
}
public boolean setHighSpeedMode(boolean flag){
mSetHeightSpeedModeResult = false;
if (mGatt==null)
return false;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return false;
int mtuSize = 3;
if (flag){
mtuSize += MAX_SINGLE_SZIE;
}else {
mtuSize += DEFAULT_SINGLE_SZIE;
}
synchronized (mGatt){
boolean res = mGatt.requestMtu(mtuSize);
Logs.d("LPQ", "request Max Mtu " + (mtuSize) + " result:"+res);
if (!res)
return false;
try {
mGatt.wait(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return mSetHeightSpeedModeResult;
}
/**
* 处理设备连接事件,所有BLE连接状态事件最终处理的地方
*
* @param tryConnect 是否为尝试连接事件
* @param connectState 连接状态,连接或断开
*/
private synchronized void handleConnectEvent(boolean tryConnect, boolean connectState) {
if (tryConnect) {
setConnecting(false);
if (connectState) {
setConnected(true);
} else {
//连接失败
setConnected(false);
if (mGatt != null) {
mGatt.disconnect();
mGatt.close();
mGatt = null;
}
}
sendConnectResultMessage(connectState);
} else {
//这种情况一般connectState只可能为false,即蓝牙已经成功连接后断开的情况
if (mGatt != null) {
// mGatt.disconnect();
mGatt.close();
mGatt = null;
}
if (isConnected()) {
setConnected(false);
sendDisconnectResultMessage();
}
}
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/helper/BluetoothHelper.java
================================================
package com.stag.bluetooth.helper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import com.stag.bluetooth.BluetoothTransfer;
import com.stag.bluetooth.OnBluetoothConnectStateChangeListener;
import com.stag.bluetooth.OnBluetoothTransmitListener;
import com.stag.bluetooth.protocol.Protocol;
/**
* Created by Administrator on 2016/11/14.
*/
public abstract class BluetoothHelper {
protected static final int CONNECT_TIMEOUT = 10000; //连接超时时间
private static final int HANDLER_CONNECT_EVENT = 1;
private static final int HANDLER_DISCONNECT_EVENT = 2;
protected Context mContext;
protected boolean isConnected;
protected boolean isConnecting; //是否为尝试连接,用于判断连接断开的情况:连接失败或者已经连接成功后断开
protected BluetoothAdapter mBluetoothAdapter;
protected BluetoothDevice mDevice;
protected OnBluetoothConnectStateChangeListener listener;
protected OnBluetoothTransmitListener transmitListener;//蓝牙数据收发监听
protected Protocol mProtocol;
private Handler handler;
protected BluetoothHelper(Context context){
mContext = context;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
public abstract void startScan();
public abstract void stopScan();
/**
* BLE和传统都为改为异步进行
* */
public abstract void connect(String address);
public abstract void disconnect();
public void send(byte[] data){
if (transmitListener!=null)
transmitListener.onBluetoothSendData(data);
}
/**
* 接收数据,不可主动调用
* */
protected void recv(byte[] data){
if (data==null||data.length==0)
return;
if (transmitListener!=null)
transmitListener.onBluetoothRecvData(data);
BluetoothTransfer.getInstance().addRecvData(data);
}
protected void configHandler(){
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case HANDLER_CONNECT_EVENT:
/*final boolean isSuccess = (Boolean) msg.obj;
if (isSuccess){
postDelayed(new Runnable() {
@Override
public void run() {
connectCallback(isSuccess);
}
}, 10);
}else {
connectCallback(isSuccess);
}*/
connectCallback((Boolean) msg.obj);
break;
case HANDLER_DISCONNECT_EVENT:
disconnectCallback();
break;
}
}
};
}
protected void sendConnectResultMessage(boolean isSuccess){
Message.obtain(handler, HANDLER_CONNECT_EVENT, isSuccess).sendToTarget();
}
protected void sendDisconnectResultMessage(){
Message.obtain(handler, HANDLER_DISCONNECT_EVENT).sendToTarget();
}
/**
* 连接回调
* @param isSuccess 是否成功
* */
private void connectCallback(boolean isSuccess){
if (listener!=null)
listener.onBluetoothConnect(mDevice, isSuccess);
}
/**
* 断开回调
* */
private void disconnectCallback(){
if (listener!=null)
listener.onBluetoothDisconnect(mDevice);
}
public boolean isConnected() {
return isConnected;
}
public void setConnected(boolean connected) {
isConnected = connected;
}
public boolean isConnecting() {
return isConnecting;
}
public void setConnecting(boolean connecting) {
isConnecting = connecting;
}
public OnBluetoothConnectStateChangeListener getConnectStateChangeListener() {
return listener;
}
public void setConnectStateChangeListener(OnBluetoothConnectStateChangeListener listener) {
this.listener = listener;
}
public OnBluetoothTransmitListener getTransmitListener() {
return transmitListener;
}
public void setTransmitListener(OnBluetoothTransmitListener transmitListener) {
this.transmitListener = transmitListener;
}
public void setProtocol(Protocol protocol){
mProtocol = protocol;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/helper/MyBluetoothService.java
================================================
package com.stag.bluetooth.helper;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.stag.bluetooth.util.ByteUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MyBluetoothService {
private static final String TAG = "MyBluetoothService";
private static Handler handler; // handler that gets info from Bluetooth service
// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
public static final int MESSAGE_READ = 0;
public static final int MESSAGE_WRITE = 1;
public static final int MESSAGE_TOAST = 2;
// ... (Add other message types here as needed.)
}
public static class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams; using temp objects because
// member streams are final.
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Log.d(TAG + "lpq", "run: 收到数据: " + numBytes);
for (int i = 0; i < numBytes; i++) {
int len = mmInStream.read(mmBuffer, 0, numBytes);
Log.d(TAG + "lpq", "run: bytes = " + ByteUtils.toString(mmBuffer));
}
// Message readMsg = handler.obtainMessage(
// MessageConstants.MESSAGE_READ, numBytes, -1,
// mmBuffer);
// readMsg.sendToTarget();
} catch (IOException e) {
Log.d(TAG + "lpq", "Input stream was disconnected", e);
break;
}
}
}
// Call this from the main activity to send data to the remote device.
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
// Share the sent message with the UI activity.
Message writtenMsg = handler.obtainMessage(
MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
writtenMsg.sendToTarget();
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when sending data", e);
// Send a failure message back to the activity.
Message writeErrorMsg =
handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast",
"Couldn't send data to the other device");
writeErrorMsg.setData(bundle);
handler.sendMessage(writeErrorMsg);
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG + "lpq", "Could not close the connect socket", e);
}
}
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/helper/TraditionHelper.java
================================================
package com.stag.bluetooth.helper;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import com.stag.bluetooth.util.ByteUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.UUID;
/**
* Created by Administrator on 2016/11/14.
*/
public final class TraditionHelper extends BluetoothHelper {
private final static UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // SPP服务UUID号
private static TraditionHelper instance;
private BluetoothSocket bthSocket;
private InputStream is;
private OutputStream os;
public static TraditionHelper getInstance(Context context){
if (instance==null){
synchronized (TraditionHelper.class){
instance = new TraditionHelper(context);
}
}
return instance;
}
private TraditionHelper(Context context){
super(context);
}
@Override
public void startScan() {
mBluetoothAdapter.startDiscovery();
}
@Override
public void stopScan() {
mBluetoothAdapter.cancelDiscovery();
}
@Override
public void connect(final String address) {
setConnecting(true);
configHandler();
new Thread(){
@Override
public void run() {
super.run();
mDevice = mBluetoothAdapter.getRemoteDevice(address);
try {
bthSocket = mDevice.createRfcommSocketToServiceRecord(SPP_UUID);
bthSocket.connect();
is = bthSocket.getInputStream();
os = bthSocket.getOutputStream();
} catch (IOException e) {
try {
bthSocket =(BluetoothSocket) mDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(mDevice,1);
bthSocket.connect();
is = bthSocket.getInputStream();
os = bthSocket.getOutputStream();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
sendConnectResultMessage(false);
return;
} catch (InvocationTargetException e1) {
e1.printStackTrace();
sendConnectResultMessage(false);
return;
} catch (NoSuchMethodException e1) {
e1.printStackTrace();
sendConnectResultMessage(false);
return;
} catch (IOException e1) {
e1.printStackTrace();
sendConnectResultMessage(false);
return;
} finally {
setConnecting(false);
}
}
setConnected(true);
new Thread(readThread).start();
sendConnectResultMessage(true);
}
}.start();
}
@Override
public void disconnect() {
if (isConnected()){
setConnected(false);
if (bthSocket != null) {
try {
is.close();
os.close();
bthSocket.close();
bthSocket=null;
is=null;
os=null;
} catch (IOException e) {
e.printStackTrace();
}
}
sendDisconnectResultMessage();
}
}
private Runnable readThread = new Runnable() {
@Override
public void run() {
byte[] buffer = new byte[1024];
int num;
while (isConnected()) {
try {
// if (is.available() > 0) {
num = is.read(buffer, 0, 1024);
if (num > 0) {
recv(ByteUtils.subBytes(buffer, 0, num));
}
// } else {
// Thread.sleep(1);
// }
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
@Override
public void send(byte[] data) {
super.send(data);
if (data==null || data.length==0)
return;
if (os != null) {
try {
os.write(data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/helper/TraditionServerHelper.java
================================================
package com.stag.bluetooth.helper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.util.Log;
import com.stag.bluetooth.util.ByteUtils;
import com.stag.bluetooth.util.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
public class TraditionServerHelper extends BluetoothHelper {
private static final String TAG = "TraditionServerHelp-";
private static final String NAME = "XGD N6";
private final static UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // SPP服务UUID号
private static TraditionServerHelper instance;
public static TraditionServerHelper getInstance(Context context) {
if (instance == null) {
synchronized (TraditionServerHelper.class) {
instance = new TraditionServerHelper(context);
}
}
return instance;
}
private TraditionServerHelper(Context context) {
super(context);
acceptThread = new AcceptThread();
}
@Override
public void startScan() {
LogUtils.d(TAG + "lpq", "startScan: 不可调用");
if (acceptThread == null) {
acceptThread = new AcceptThread();
}
acceptThread.start();
configHandler();
}
@Override
public void stopScan() {
LogUtils.d(TAG + "lpq", "stopScan: 不可调用");
if (acceptThread != null) {
acceptThread.cancel();
acceptThread = null;
}
}
@Override
public void connect(String address) {
LogUtils.d(TAG + "lpq", "connect: 不可调用");
}
@Override
public void disconnect() {
LogUtils.d(TAG + "lpq", "disconnect: 不可调用");
if (connectedThread != null) {
connectedThread.cancel();
}
}
private AcceptThread acceptThread;
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket
// because mmServerSocket is final.
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code.
tmp = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommWithServiceRecord(NAME, SPP_UUID);
} catch (IOException e) {
Log.e(TAG + "lpq", "Socket's listen() method failed", e);
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned.
while (true) {
try {
setConnecting(true);
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG + "lpq", "等待连接时发生异常", e);
break;
}
setConnecting(false);
if (socket != null) {
// A connection was accepted. Perform work associated with
// the connection in a separate thread.
// manageMyConnectedSocket(socket);
connectedThread = new ConnectedThread(socket);
connectedThread.start();
setConnected(true);
sendConnectResultMessage(true);
Log.d(TAG + "lpq", "run: 蓝牙连接成功");
try {
mmServerSocket.close();
break;
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
// Closes the connect socket and causes the thread to finish.
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG + "lpq", "Could not close the connect socket", e);
}
}
}
private ConnectedThread connectedThread;
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams; using temp objects because
// member streams are final.
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer, 0, 1024);
Log.d(TAG + "lpq", "run: 收到数据: " + numBytes);
if (numBytes > 0) {
recv(ByteUtils.subBytes(mmBuffer, 0, numBytes));
}
// Message readMsg = handler.obtainMessage(
// MessageConstants.MESSAGE_READ, numBytes, -1,
// mmBuffer);
// readMsg.sendToTarget();
} catch (IOException e) {
Log.d(TAG + "lpq", "Input stream was disconnected", e);
break;
}
}
}
// Call this from the main activity to send data to the remote device.
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
// Share the sent message with the UI activity.
} catch (IOException e) {
Log.e(TAG + "lpq", "Error occurred when sending data", e);
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
try {
mmSocket.close();
sendDisconnectResultMessage();
} catch (IOException e) {
Log.e(TAG + "lpq", "Could not close the connect socket", e);
}
}
}
@Override
public void send(byte[] data) {
super.send(data);
if (data == null || data.length == 0)
return;
if (connectedThread != null && connectedThread.mmOutStream != null) {
try {
connectedThread.mmOutStream.write(data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/packet/Packet.java
================================================
package com.stag.bluetooth.packet;
/**
* Created by Administrator on 2016/11/14.
*/
public class Packet{
protected int cmd; //命令
protected byte[] data;//数据(包含的主要信息)
// protected int serialNumber;//流水号,暂时业务不需要
public Packet(int cmd) {
this(cmd, new byte[0]);
}
public Packet(int cmd, byte[] data) {
this.cmd = cmd;
this.data = data==null?new byte[0]:data;
}
/**
* 用来与接收包进行匹配判断
* @param recvPacket 接收到的字节数据处理的结果包
* */
public boolean match(T recvPacket){
return cmd==recvPacket.cmd;
}
public int getCmd() {
return cmd;
}
public void setCmd(int cmd) {
this.cmd = cmd;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/protocol/OnEventListener.java
================================================
package com.stag.bluetooth.protocol;
/**
* 监听蓝牙设备的主动上传事件,不同协议各不相同
* Created by Administrator on 2016/11/15.
*/
public interface OnEventListener {
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/protocol/ParseResult.java
================================================
package com.stag.bluetooth.protocol;
import com.stag.bluetooth.BluetoothDispatch;
import com.stag.bluetooth.packet.Packet;
/**
* 蓝牙收到的数据解析结果
* Created by Administrator on 2016/11/15.
*/
public final class ParseResult {
private ResultType type;
private Packet packet;
private BluetoothDispatch.Callback callback; //主动事件才不为null
public ParseResult(){
};
public ParseResult(ResultType type, Packet packet, BluetoothDispatch.Callback callback) {
this.type = type;
this.packet = packet;
this.callback = callback;
}
public ResultType getType() {
return type;
}
public void setType(ResultType type) {
this.type = type;
}
public Packet getPacket() {
return packet;
}
public void setPacket(Packet packet) {
this.packet = packet;
}
public BluetoothDispatch.Callback getCallback() {
return callback;
}
public void setCallback(BluetoothDispatch.Callback callback) {
this.callback = callback;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/protocol/Protocol.java
================================================
package com.stag.bluetooth.protocol;
import android.content.Context;
import com.stag.bluetooth.packet.Packet;
import java.util.UUID;
/**
* Created by Administrator on 2016/11/14.
*/
public abstract class Protocol {
public final static int BLE_MAX_SEND_INTERVAL = 500;
protected Context mContext;
private T mEventListener;
private Object mData;
private int mMaxBleSendInterval = BLE_MAX_SEND_INTERVAL;
/**
* 协议所特有的主动事件监听
*/
protected Protocol(Context context) {
this(context, null);
}
/**
* 协议所特有的主动事件监听
*/
protected Protocol(Context context, T listener) {
mContext = context.getApplicationContext();
mEventListener = listener;
}
/**
* 初始化
* */
public void initialize(){
}
/**
* 销毁
* */
public void destroy(){
mContext = null;
mEventListener = null;
}
/**
* 发送包处理成最终要发送的字节数据
*/
public abstract byte[] packetToBytes(E packet);
/**
* 解析收到的字节处理成结果
*/
public abstract ParseResult parse(byte[] data);
/**
* 获取协议类型
* */
public abstract int getType();
/**
* 是否设置了主动事件监听
* */
protected boolean haveSetEventListener(){
return mEventListener!=null;
}
public UUID getServiceUUID() {
return UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
}
public UUID getSendTunnelUUID() {
return UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb");
}
public UUID getRecvTunnelUUID() {
return UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb");
}
public UUID getDescriptorUUID() {
return UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
}
/**
* 建议最大发送间隔
* */
public void setMaxBleSendInterval(int interval) {
mMaxBleSendInterval = interval;
}
/**
* 有的协议对应目标硬件接收间隔有限制,例如生久
* */
public int getMaxBleSendInterval() {
return mMaxBleSendInterval;
}
public T getEventListener() {
return mEventListener;
}
public void setEventListener(T eventListener) {
this.mEventListener = eventListener;
}
public Object getData() {
return mData;
}
public void setData(Object data) {
this.mData = data;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/protocol/ResultType.java
================================================
package com.stag.bluetooth.protocol;
/**
* Created by Administrator on 2016/11/28.
*/
public enum ResultType {
RESPOND, //响应事件
ACTIVE, //主动事件
INCOMPLETE //非完整一帧
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/util/ByteUtils.java
================================================
package com.stag.bluetooth.util;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Created by Administrator on 2016/7/25 0025.
*/
public final class ByteUtils {
/**
* 比较两个byte数组
*
* @param first
* @param second
* @return
*/
public static boolean compareBytes(byte[] first, byte[] second) {
for (int i = 0; i < (first.length < second.length ? first.length : second.length); i++) {
if (first[i] != second[i]) {
return false;
}
}
return true && first.length == second.length;
}
/**
* 缺失部分有效数字
* long型转成4字节byte 大端模式
*
* @param l
* @return
*/
public static byte[] longToByte4BigEndian(long l) {
byte[] b = new byte[4];
b[3] = (byte) (l & 0xffL);
b[2] = (byte) ((l >> 8) & 0xff00L);
b[1] = (byte) ((l >> 16) & 0xff0000L);
b[0] = (byte) ((l >> 24) & 0xff000000L);
return b;
}
/**
* 4字节byte转化成Long 大端模式
*
* @param b
* @return
*/
public static long byte4ToLongBigEndian(byte[] b) {
long l = (long)b[3] & 0xffL;
l |= ((long)b[2] << 8) & 0xff00L;
l |= ((long)b[1] << 16) & 0xff0000L;
l |= ((long)b[0] << 24) & 0xff000000L;
return l;
}
/**
* 16个字节转化UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte16ToUuidBigEndian(byte[] b) {
return new UUID(byte8ToLongBigEndian(subBytes(b, 0, 8)), byte8ToLongBigEndian(subBytes(b, 8, 8)));
}
/**
* UUID转化成16个字节 大端模式
*
* @param uuid
* @return
*/
public static byte[] uuidToByte16BigEndian(UUID uuid) {
long l = uuid.getLeastSignificantBits();
long l2 = uuid.getMostSignificantBits();
return combineByteArray(longToByte8BigEndian(l2), longToByte8BigEndian(l));
}
/**
* 8个字节转化UUID 大端模式
*
* @param b
* @return
*/
public static UUID byte8ToUuidBigEndian(byte[] b) {
return new UUID(0, byte8ToLongBigEndian(b));
}
/**
* UUID转化成8个字节 大端模式
*
* @param uuid
* @return
*/
public static byte[] uuidToByte8BigEndian(UUID uuid) {
long l = uuid.getLeastSignificantBits();
return longToByte8BigEndian(l);
}
/**
* 8个字节转化成long型 大端模式
*
* @param b
* @return
*/
public static long byte8ToLongBigEndian(byte[] b) {
long l = (long)b[7] & 0xffL;
l |= ((long)b[6] << 8) & 0xff00L;
l |= ((long)b[5] << 16) & 0xff0000L;
l |= ((long)b[4] << 24) & 0xff000000L;
l |= ((long)b[3] << 32) & 0xff00000000L;
l |= ((long)b[2] << 40) & 0xff0000000000L;
l |= ((long)b[1] << 48) & 0xff000000000000L;
l |= ((long)b[0] << 56) & 0xff00000000000000L;
return l;
}
/**
* long型转成8个字节 大端模式
*
* @param l
* @return
*/
public static byte[] longToByte8BigEndian(long l) {
byte[] b = new byte[8];
b[7] = (byte) (l & 0xffL);
b[6] = (byte) ((l >> 8) & 0xffL);
b[5] = (byte) ((l >> 16) & 0xffL);
b[4] = (byte) ((l >> 24) & 0xffL);
b[3] = (byte) ((l >> 32) & 0xffL);
b[2] = (byte) ((l >> 40) & 0xffL);
b[1] = (byte) ((l >> 48) & 0xffL);
b[0] = (byte) ((l >> 56) & 0xffL);
return b;
}
/**
* 大端模式将4个字节的Byte转成int型
*
* @param b
* @return
*/
public static int byte4ToIntBigEndian(byte[] b) {
int i = b[3] & 0xff;
i |= ((b[2] << 8) & 0xff00);
i |= ((b[1] << 16) & 0xff0000);
i |= ((b[0] << 24) & 0xff000000);
return i;
}
/**
* 大端模式将int型转成4个字节的Byte
*
* @param i
* @return
*/
public static byte[] intToByte4BigEndian(int i) {
byte[] res = new byte[4];
res[3] = (byte) (i & 0xff);
res[2] = (byte) ((i >> 8) & 0xff);
res[1] = (byte) ((i >> 16) & 0xff);
res[0] = (byte) ((i >> 24) & 0xff);
return res;
}
/**
* 大端模式将3个字节的Byte转成int型
*
* @param b
* @return
*/
public static int byte3ToIntBigEndian(byte[] b) {
int i = b[2] & 0xff;
i |= ((b[1] << 8) & 0xff00);
i |= ((b[1] << 16) & 0xff0000);
return i;
}
/**
* 大端模式将int型转成3个字节的Byte
*
* @param i
* @return
*/
public static byte[] intToByte3BigEndian(int i) {
byte[] res = new byte[3];
res[2] = (byte) (i & 0xff);
res[1] = (byte) ((i >> 8) & 0xff);
res[0] = (byte) ((i >> 16) & 0xff);
return res;
}
public static String toString(byte[] data) {
if (data == null) {
return null;
}
StringBuffer buffer = new StringBuffer();
for (byte b : data) {
String d = Integer.toHexString(b & 0xff);
if (d.length() > 1) {
buffer.append(d + " ");
} else {
buffer.append("0" + d + " ");
}
}
return buffer.toString();
}
public static byte[] subBytes(byte[] data, int start, int len) {
byte[] res = new byte[len];
for (int i = 0; i < len; i++) {
res[i] = data[start + i];
}
return res;
}
public static byte[] subBytes(byte[] data, int start) {
int len = data.length - start;
return subBytes(data, start, len);
}
/**
* 将字节数组转化成Long
*
* @param b
* @return
*/
public static long bytesToLong(byte[] b) {
long l = ((long) b[0] << 56) & 0xFF00000000000000L;
// 如果不强制转换为long,那么默认会当作int,导致最高32位丢失
l |= ((long) b[1] << 48) & 0xFF000000000000L;
l |= ((long) b[2] << 40) & 0xFF0000000000L;
l |= ((long) b[3] << 32) & 0xFF00000000L;
l |= ((long) b[4] << 24) & 0xFF000000L;
l |= ((long) b[5] << 16) & 0xFF0000L;
l |= ((long) b[6] << 8) & 0xFF00L;
l |= b[7] & 0xFFL;
return l;
}
/**
* 将Long转化成字节数组
*
* @param l
* @return
*/
public static byte[] longToBytes(long l) {
byte[] b = new byte[8];
b[0] = (byte) (l >>> 56);
b[1] = (byte) (l >>> 48);
b[2] = (byte) (l >>> 40);
b[3] = (byte) (l >>> 32);
b[4] = (byte) (l >>> 24);
b[5] = (byte) (l >>> 16);
b[6] = (byte) (l >>> 8);
b[7] = (byte) (l);
return b;
}
/**
* 合并Byte数组
*
* @param bytes
* @return
*/
public static byte[] combineByteArray(byte[]... bytes) {
int len = 0;
for (byte[] bs : bytes) {
if (bs != null) {
len += bs.length;
}
}
byte[] res = new byte[len];
int pos = 0;
for (byte[] bs : bytes) {
if (bs != null) {
for (byte b : bs) {
res[pos++] = b;
}
}
}
return res;
}
/**
* 将UUID转化成Byte数组
*
* @param uuid
* @return
*/
public static byte[] uuidToBytes(UUID uuid) {
return combineByteArray(longToBytes(uuid.getMostSignificantBits()), longToBytes(uuid.getLeastSignificantBits()));
}
/**
* 将Byte数组转化成UUID
*
* @param data
* @return
*/
public static UUID bytesToUuid(byte[] data) {
if (data == null || data.length < 16)
return null;
byte[] bMost = subBytes(data, 0, 8);
byte[] bLeast = subBytes(data, 8, 8);
return new UUID(bytesToLong(bMost), bytesToLong(bLeast));
}
/**
* 将Int转化为Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytes2(int i) {
byte[] res = new byte[2];
res[1] = (byte) (i & 0xFF);
res[0] = (byte) (i >> 8 & 0xFF);
return res;
}
public static byte[] intToBytes2SmallDian(int i) {
byte[] res = new byte[2];
res[0] = (byte) (i & 0xFF);
res[1] = (byte) (i >> 8 & 0xFF);
return res;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToIntBigEndian(byte[] b) {
int res = (int) ((b[0] << 24) & 0xFF000000L);
res |= (b[1] << 16 & 0xFF0000L);
res |= (b[2] << 8 & 0xFF00L);
res |= (b[3] & 0xFFL);
return res;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToIntSmallEndian(byte[] b) {
int res = (int) ((b[3] << 24) & 0xFF000000L);
res |= (b[2] << 16 & 0xFF0000L);
res |= (b[1] << 8 & 0xFF00L);
res |= (b[0] & 0xFFL);
return res;
}
/**
* 将Int转化成Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytesBigEndian(int i) {
byte[] res = new byte[4];
res[0] = (byte) ((i >> 24) & 0xFFL);
res[1] = (byte) ((i >> 16) & 0xFFL);
res[2] = (byte) ((i >> 8) & 0xFFL);
res[3] = (byte) (i & 0xFFL);
return res;
}
/**
* 将Byte数组转化为int
*
* @param b
* @return
*/
public static int bytesToInt(byte[] b) {
int res = (int) ((b[3] << 24) & 0xFF000000L);
res |= (b[2] << 16 & 0xFF0000L);
res |= (b[1] << 8 & 0xFF00L);
res |= (b[0] & 0xFFL);
return res;
}
/**
* 将Int转化成Byte数组
*
* @param i
* @return
*/
public static byte[] intToBytes(int i) {
byte[] res = new byte[4];
res[3] = (byte) ((i >> 24) & 0xFFL);
res[2] = (byte) ((i >> 16) & 0xFFL);
res[1] = (byte) ((i >> 8) & 0xFFL);
res[0] = (byte) (i & 0xFFL);
return res;
}
/**
* 将Byte数组转化成short
*
* @param b
* @return
*/
public static short bytesToShort(byte[] b) {
short res = (short) ((b[1] << 8) & 0xFF00L);
res |= (b[0] & 0xFFL);
return res;
}
/**
* 将Short转化为Byte数组
*
* @param i
* @return
*/
public static byte[] shortToBytes(short i) {
byte[] res = new byte[2];
res[1] = (byte) ((i >> 8) & 0xFFL);
res[0] = (byte) (i & 0xFFL);
return res;
}
/**
* 大端序
* 将Byte数组转化成short
*
* @param b
* @return
*/
public static short bytesToShortBigEndian(byte[] b) {
short res = (short) ((b[0] << 8) & 0xFF00L);
res |= (b[1] & 0xFFL);
return res;
}
/**
* 大端序
* 将Short转化为Byte数组
*
* @param i
* @return
*/
public static byte[] shortToBytesBigEndian(short i) {
byte[] res = new byte[2];
res[0] = (byte) ((i >> 8) & 0xFFL);
res[1] = (byte) (i & 0xFFL);
return res;
}
/**
* 填充数据至长度为N的数组
*
* @param data
* @param len
* @return
*/
public static byte[] wrapData(byte[] data, int len) {
byte[] res = new byte[len];
for (int i = 0; i < len; i++) {
if (i < data.length) {
res[i] = data[i];
} else {
res[i] = 0;
}
}
return res;
}
/**
* 取出一字节中每一位的数值
*
* @param data
* @return
*/
public static byte[] getByteBits(byte data) {
byte[] result = new byte[8];
for (int i = 0; i < 8; i++) {
result[i] = (byte) (data >> i & 0x01);
}
return result;
}
public static byte[] getBytesWithBuffer(ByteBuffer buffer) {
if (buffer == null)
return null;
byte[] bytes = new byte[buffer.position()];
for (int i = 0; i < buffer.position(); i++)
bytes[i] = buffer.array()[i];
return bytes;
}
/**
* 获取bytes的Ascii码对应的字符串
* @param bytes
* @return
*/
public static String getAsciiString(byte[] bytes) {
if (bytes == null) {
return null;
}
String result = "";
for (int i = 0; i < bytes.length; ++i) {
result += (char)bytes[i];
}
return result;
}
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* byte[] To list
* @param data
* @return
*/
public static List byteToList(byte[] data) {
List list = null;
if (data != null && data.length > 0) {
list = new ArrayList<>();
for (int i = 0; i < data.length; ++i) {
list.add(data[i]);
}
}
return list;
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/util/LogUtils.java
================================================
package com.stag.bluetooth.util;
import android.util.Log;
import com.stag.bluetooth.BuildConfig;
public class LogUtils {
private static boolean isDebug = true;
private LogUtils() {
}
public static void v(String tag, String msg) {
if (isDebug) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
if (isDebug) {
//信息太长,分段打印
//因为String的length是字符数量不是字节数量所以为了防止中文字符过多,
// 把4*1024的MAX字节打印长度改为2001字符数
int max_str_length = 2001 - tag.length();
//大于4000时
while (msg.length() > max_str_length) {
Log.d(tag, msg.substring(0, max_str_length));
msg = msg.substring(max_str_length);
}
//剩余部分
Log.d(tag, msg);
}
}
public static void i(String tag, String msg) {
if (isDebug) {
Log.i(tag, msg);
}
}
public static void w(String tag, String msg) {
if (isDebug) {
Log.w(tag, msg);
}
}
public static void e(String tag, String msg) {
if (isDebug) {
Log.e(tag, msg);
}
}
}
================================================
FILE: bluetooth/src/main/java/com/stag/bluetooth/util/Logs.java
================================================
package com.stag.bluetooth.util;
import android.util.Log;
/**
* Created by Administrator on 2017/6/21.
*/
public class Logs {
private static final boolean DEBUG = true;
public static void d(String tag, String msg){
if (DEBUG)
Log.d(tag, msg);
}
public static void e(String tag, String msg){
if (DEBUG)
Log.e(tag, msg);
}
}
================================================
FILE: bluetooth/src/main/res/values/strings.xml
================================================
Bluetooth
================================================
FILE: bluetooth/src/test/java/com/stag/bluetooth/ExampleUnitTest.java
================================================
package com.stag.bluetooth;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.tencent.bugly:symtabfileuploader:latest.release'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Sat Apr 18 17:08:04 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
rootProject.name='Bluetooth'
include ':app', ':bluetooth'