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 ================================================
xmlns:android ^$
xmlns:.* ^$ BY_NAME
.*:id http://schemas.android.com/apk/res/android
.*:name http://schemas.android.com/apk/res/android
name ^$
style ^$
.* ^$ BY_NAME
.* http://schemas.android.com/apk/res/android ANDROID_ATTRIBUTE_ORDER
.* .* BY_NAME
================================================ 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以上的手机需要定位权限才能正常使用蓝牙功能** ## 库地址 [ ![Download](https://api.bintray.com/packages/luopeiqin/maven/bluetooth/images/download.svg?version=1.0.2) ](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 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 ================================================