[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\n"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <codeStyleSettings language=\"XML\">\n      <indentOptions>\n        <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n      </indentOptions>\n      <arrangement>\n        <rules>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:android</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:id</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>style</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>ANDROID_ATTRIBUTE_ORDER</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>.*</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n        </rules>\n      </arrangement>\n    </codeStyleSettings>\n  </code_scheme>\n</component>"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <annotationProcessing>\n      <profile default=\"true\" name=\"Default\" enabled=\"true\" />\n    </annotationProcessing>\n    <bytecodeTargetLevel target=\"1.8\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleMigrationSettings\" migrationVersion=\"1\" />\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"testRunner\" value=\"GRADLE\" />\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n            <option value=\"$PROJECT_DIR$/bluetooth\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/jarRepositories.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RemoteRepositoriesConfiguration\">\n    <remote-repository>\n      <option name=\"id\" value=\"central\" />\n      <option name=\"name\" value=\"Maven Central repository\" />\n      <option name=\"url\" value=\"https://repo1.maven.org/maven2\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"jboss.community\" />\n      <option name=\"name\" value=\"JBoss Community repository\" />\n      <option name=\"url\" value=\"https://repository.jboss.org/nexus/content/repositories/public/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"BintrayJCenter\" />\n      <option name=\"name\" value=\"BintrayJCenter\" />\n      <option name=\"url\" value=\"https://jcenter.bintray.com/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"Google\" />\n      <option name=\"name\" value=\"Google\" />\n      <option name=\"url\" value=\"https://dl.google.com/dl/android/maven2/\" />\n    </remote-repository>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_8\" default=\"true\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "<img src=\"https://github.com/LuoPeiQin/Bluetooth/blob/master/app/src/main/res/mipmap-xhdpi/logo.png\" width=\"20%\">\n\n# Bluetooth\n## 该库的特点\n\n1. 内部集成了多种蓝牙芯片的操作，能兼容几乎市面上所有的蓝牙设备；\n2. 支持低功耗蓝牙和传统蓝牙；\n3. 支持设置低功耗蓝牙的高速传输模式；\n4. 实现了蓝牙的重发机制；\n5. 实现了蓝牙的同异步发送数据；\n6. 实现了协议基类封装，开发者可以快速扩展自己的协议；\n\n**注意：部分Android6.0以上的手机需要定位权限才能正常使用蓝牙功能**\n\n## 库地址\n[ ![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)\n```\nimplementation 'com.stag:bluetooth:1.0.2'\n```\n## 蓝牙搜索自定义View\n项目中增加了蓝牙搜素的自定义View，方便你快速的实现项目\n### 自定义View包含的内容\n1. 根据蓝牙的信号强度排序和显示；\n2. 有搜素或连接的历史记录功能（做小部分配置即可实现）；\n<img src=\"https://github.com/LuoPeiQin/Bluetooth/blob/master/%E7%A4%BA%E4%BE%8B1.jpg\" width=\"50%\">\n\n## 蓝牙基本操作相关接口说明\n\n> **调用的类为com.stag.bluetooth.BluetoothControl.java**\n\n### 获取单例对象\n\n```java\nBluetoothController mController = BluetoothController.getController(this);\n```\n\n### 注册蓝牙状态监听\n\n```java\nmController.registerBluetoothStateChangeListener(new OnBluetoothStateChangeListener() {\n    @Override\n    public void onBluetoothOpen() {\n\tLogUtils.i(TAG + \"lpq\", \"onBluetoothOpen: 蓝牙打开\");\n    }\n\n    @Override\n    public void onBluetoothClose() {\n\tLogUtils.i(TAG + \"lpq\", \"onBluetoothClose: 蓝牙关闭\");\n    }\n});\n```\n\n### 注册蓝牙连接状态变化监听\n\n```java\nmController.registerConnectStateChangeListener(new OnBluetoothConnectStateChangeListener() {\n    @Override\n    public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {\n\tif (isSuccess) {\n\t    LogUtils.i(TAG + \"lpq\", \"onBluetoothConnect: 蓝牙已连接\");\n\t} else {\n\t    LogUtils.i(TAG + \"lpq\", \"onBluetoothConnect: 蓝牙连接失败\");\n\t}\n    }\n\n    @Override\n    public void onBluetoothDisconnect(BluetoothDevice device) {\n\tLogUtils.i(TAG + \"lpq\", \"onBluetoothDisconnect: 蓝牙已断开\");\n    }\n});\n```\n\n### 切换蓝牙类型\n\n```java\nmController.setBluetoothType(BluetoothType.BLE); // 低功耗蓝牙\nmController.setBluetoothType(BluetoothType.TRADITION); // 传统蓝牙\n```\n\n### 搜索蓝牙\n\n```java\nmController.startScan(new OnBluetoothScanListener() {\n    @Override\n    public void onBluetoothScanFindDevice(BluetoothDevice device, int rssi) {\n\tLogUtils.i(TAG + \"lpq\", \"onBluetoothScanFindDevice: \" + device.getAddress());\n    }\n\n    @Override\n    public void onBluetoothScanFinish() {\n\tLogUtils.i(TAG + \"lpq\", \"onBluetoothScanFinish: \");\n    }\n});\n```\n\n### 停止搜索蓝牙\n\n```java\nmController.stopScan();\n```\n\n### 连接蓝牙与断开蓝牙连接\n\n```java\nmController.connect(\"0C:B2:B7:3E:23:60\");\t//连接蓝牙，参数为蓝牙MAC地址\nmController.disconnect();\t//断开连接\n```\n\n### 低功耗蓝牙时开启高速模式\n\n```java\nmController.setBleHighSpeedMode(true);\n```\n\n不一定100%成功，由蓝牙设备与App设备蓝牙的最低MTU决定。\n\n### 取消注册监听回调\n\n```java\n@Override\nprotected void onDestroy() {\n\tsuper.onDestroy();\n\tif (mController != null) {\n\t    mController.unregisterBluetoothStateChangeListener();\n\t    mController.unregisterConnectStateChangeListener();\n\t}\n}\n```\n\n## 蓝牙收发数据重点说明\n\n### 方式一：继承蓝牙内置协议\n\n```java\npublic abstract class Protocol<E extends Packet, T extends OnEventListener> {\n    public final static int BLE_MAX_SEND_INTERVAL = 500;\n    protected Context mContext;\n    private T mEventListener;\n    private Object mData;\n    private int mMaxBleSendInterval = BLE_MAX_SEND_INTERVAL;\n\n    /**\n     * 协议所特有的主动事件监听\n     */\n    protected Protocol(Context context) {\n        this(context, null);\n    }\n\n    /**\n     * 协议所特有的主动事件监听\n     */\n    protected Protocol(Context context, T listener) {\n        mContext = context.getApplicationContext();\n        mEventListener = listener;\n    }\n\n    /**\n     * 发送包处理成最终要发送的字节数据\n     */\n    public abstract byte[] packetToBytes(E packet);\n\n    /**\n     * 解析收到的字节处理成结果\n     */\n    public abstract ParseResult parse(byte[] data);\n\n    /**\n     * 获取协议类型\n     * */\n    public abstract int getType();\n\n    /**\n     * 是否设置了主动事件监听\n     * */\n    protected boolean haveSetEventListener(){\n        return mEventListener!=null;\n    }\n\t······\n}\n```\n\n#### 内置协议简要说明\n\n上述抽象类中屏蔽了部分内容，我们主要看几个重点：\n\n1. **packetToBytes** 和 **parse**是用户层面发送数据的最终端和接收数据的最初端，您可以根据自己蓝牙协议的需要来重写方法，创建自己的协议类；\n2. **getType**是用来支持App需要同时支持多个蓝牙协议的情况的；\n3. **mEventListener**用于接收蓝牙设备主动上报的一些状态；\n\n#### 设置蓝牙传输协议\n\n> 该方法是与蓝牙设备操作相关的方法，而且必须在连接蓝牙设备之前设置\n\n```java\nmController.setProtocol(new Protocol(this, this));\n```\n\n#### 创建异步发送数据任务\n\n```java\nBluetoothTask task = new BluetoothTask(new Packet(cmd, data), new BluetoothTask.OnDataResultListener() {\n            @Override\n            public void onResult(boolean isTimeout, byte[] data) {\n                //数据接收回调，异步时使用\n            }\n        });       \ntask.setTimeout(2000);\t//设置超时时间\ntask.setTryCount(1);\t//设置重发次数\ntask.send(); \t//异步发送数据\n```\n\n#### 创建同步发送数据任务\n\n```java\nBluetoothTask task = new BluetoothTask(new Packet(cmd, data));\ntask.setTimeout(2000);\t//设置超时时间\ntask.setTryCount(1);\t//设置重发次数\nbyte[] result = task.sendBySync2();\t//同步发送数据\n```\n\n### 方式二：原始数据收发\n\n如果方式一没有办法满足你的要求，那么我也提供方式二来供你选择\n\n#### 设置原始数据收发监听\n\n```java\n    mController.registerTransmitListener(new OnBluetoothTransmitListener() {\n        @Override\n        public void onBluetoothSendData(byte[] data) {\n            // 发送数据\n        }\n\n        @Override\n        public void onBluetoothRecvData(byte[] data) {\n            // 接收数据\n        }\n    });\n```\n\n#### 发送数据\n\n```java\nmController.sendData(new byte[]{});\n```\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'bugly'\n\nbugly {\n    appId = '4efc7ff5d2' // 注册时分配的App ID\n    appKey = '' // 注册时分配的App Key\n}\n\nandroid {\n    signingConfigs {\n        release {\n        }\n    }\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.3\"\n\n    defaultConfig {\n        applicationId \"com.luo.bluetooth\"\n        minSdkVersion 21\n        targetSdkVersion 29\n        versionCode 3\n        versionName \"1.0.2\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled true // 启用混淆\n            zipAlignEnabled true\n            shrinkResources true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n            signingConfig signingConfigs.release\n        }\n    }\n\n    compileOptions{\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    android.applicationVariants.all { variant ->\n        variant.outputs.all {\n            outputFileName = \"Bluetooth_${defaultConfig.versionName}.apk\"\n        }\n    }\n}\n\nrepositories{\n    flatDir{\n        dirs 'libs'\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation project(path: ':bluetooth')\n\n    implementation 'androidx.appcompat:appcompat:1.0.2'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n    // 注解类\n    implementation 'org.projectlombok:lombok:1.18.12'\n    android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true\n    // UI\n    implementation 'com.github.f0ris.sweetalert:library:1.5.1'\n    implementation 'androidx.cardview:cardview:1.0.0'\n    // 网络\n    implementation 'com.squareup.retrofit2:converter-gson:2.8.0'\n    implementation 'com.squareup.retrofit2:retrofit:2.8.1'\n    implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1'\n    // 工具类\n    implementation 'com.blankj:utilcodex:1.28.0'\n    // Bugly\n    implementation 'com.tencent.bugly:crashreport_upgrade:latest.release'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n# 代码混淆压缩比，在0~7之间，默认为5，一般不做修改\n-optimizationpasses 5\n\n# 混合时不使用大小写混合，混合后的类名为小写\n-dontusemixedcaseclassnames\n\n# 指定不去忽略非公共库的类\n-dontskipnonpubliclibraryclasses\n\n# 指定不去忽略非公共库的类成员\n-dontskipnonpubliclibraryclassmembers\n\n# 这句话能够使我们的项目混淆后产生映射文件\n# 包含有类名->混淆后类名的映射关系\n-verbose\n\n# 不做预校验，preverify是proguard的四个步骤之一，Android不需要preverify，去掉这一步能够加快混淆速度。\n-dontpreverify\n\n# 保留Annotation不混淆 这在JSON实体映射时非常重要，比如fastJson\n-keepattributes *Annotation*,InnerClasses\n\n# 避免混淆泛型\n-keepattributes Signature\n\n# 抛出异常时保留代码行号\n-keepattributes SourceFile,LineNumberTable\n\n# 指定混淆是采用的算法，后面的参数是一个过滤器\n# 这个过滤器是谷歌推荐的算法，一般不做更改\n-optimizations !code/simplification/cast,!field/*,!class/merging/*\n\n# 忽略警告\n-ignorewarnings\n\n# 设置是否允许改变作用域\n-allowaccessmodification\n\n# 把混淆类中的方法名也混淆了\n-useuniqueclassmembernames\n\n# apk 包内所有 class 的内部结构\n-dump class_files.txt\n\n# 未混淆的类和成员\n#-printseeds seeds_txt\n\n# 列出从apk中删除的代码\n#-printusage unused.txt\n\n#保持源码的行号、源文件信息不被混淆 方便在崩溃日志中查看\n-renamesourcefileattribute SourceFile\n-keepattributes SourceFile,LineNumberTable\n\n# 四大组件不能混淆，四大组件必须在 manifest 中注册声明\n-keep public class * extends android.app.Activity\n-keep public class * extends android.app.Application\n-keep public class * extends android.app.Service\n-keep public class * extends android.content.BroadcastReceiver\n-keep public class * extends android.content.ContentProvider\n-keep public class * extends android.app.backup.BackupAgent\n-keep public class * extends android.preference.Preference\n-keep public class * extends android.support.v4.app.Fragment\n-keep public class * extends android.app.Fragment\n-keep public class * extends android.view.view\n-keep public class com.android.vending.licensing.ILicensingService\n\n# 不能混淆枚举中的value和valueOf方法\n-keepclassmembers enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n# 不混淆bean类\n-keep class com.luo.bluetooth.blebean.** {*;}\n-keep class com.luo.bluetooth.netbean.** {*;}\n\n# gson\n-keep class com.google.gson.** {*;}\n-keep class sun.misc.Unsafe {*;}\n-keep class com.google.gson.stream.** {*;}\n-keep class com.google.gson.examples.android.model.** {*;}\n-keep class com.google.** {\n    <fields>;\n    <methods>;\n}\n-dontwarn com.google.gson.**\n\n# Bugly\n-dontwarn com.tencent.bugly.**\n-keep public class com.tencent.bugly.**{*;}\n-keep class android.support.**{*;}\n\n#ddi\n    -keep class com.xinguodu.ddiinterface.**{*;}\n    #SmartPOSJni\n        -keep class com.nexgo.oaf.smartpos.jni.SmartPOSJni{*;}\n    #API\n        -keep class com.nexgo.oaf.smartpos.CardAPI {*;}\n        -keep class com.nexgo.oaf.smartpos.DeviceAPI {*;}\n        -keep class com.nexgo.oaf.smartpos.KeyAPI {*;}\n        -keep class com.nexgo.oaf.smartpos.OtherAPI {*;}\n        -keep class com.nexgo.oaf.smartpos.PeripheralAPI {*;}\n    #DeviceEngineImpl\n        -keep class com.nexgo.oaf.smartpos.apiv3.DeviceEngineImpl {*;}\n        -keep class com.nexgo.oaf.apiv3.DeviceInfo {*;}\n\n-keep class cn.pedant.** { *; }\n"
  },
  {
    "path": "app/src/androidTest/java/com/luo/bluetooth/ExampleInstrumentedTest.java",
    "content": "package com.luo.bluetooth;\n\nimport android.content.Context;\n\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();\n\n        assertEquals(\"com.luo.bluetooth\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.luo.bluetooth\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />\n    <!-- If your app targets Android 9 or lower, you can declare\n         ACCESS_COARSE_LOCATION instead. -->\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n\n    <!--  Bugly权限  -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_LOGS\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />\n\n    <application\n        android:name=\"com.luo.bluetooth.MyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/logo\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        tools:replace=\"android:icon\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\"com.luo.bluetooth.MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"com.luo.bluetooth.customview.searchble.DeviceListActivity\"\n            android:launchMode=\"singleTask\"\n            android:theme=\"@style/DialogTheme.TransparentNoTitle\" />\n        <activity\n            android:name=\"com.tencent.bugly.beta.ui.BetaActivity\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize|locale\"\n            android:theme=\"@android:style/Theme.Translucent\" />\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/MainActivity.java",
    "content": "package com.luo.bluetooth;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.provider.Settings;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.luo.bluetooth.customview.searchble.BluetoothDeviceBean;\nimport com.luo.bluetooth.customview.searchble.DeviceListActivity;\nimport com.luo.bluetooth.protocol.CustomPacket;\nimport com.luo.bluetooth.protocol.CustomProtocol;\nimport com.luo.bluetooth.protocol.OnTimeoutResult;\nimport com.luo.bluetooth.protocol.WeiCeCode;\nimport com.luo.bluetooth.protocol.WeiCeDeviceOperate;\nimport com.luo.bluetooth.utils.ByteUtils;\nimport com.luo.bluetooth.utils.DialogUtils;\nimport com.luo.bluetooth.utils.GpsUtils;\nimport com.luo.bluetooth.utils.LogUtils;\nimport com.luo.bluetooth.utils.ToastUtil;\nimport com.stag.bluetooth.BluetoothController;\nimport com.stag.bluetooth.OnBluetoothConnectStateChangeListener;\nimport com.stag.bluetooth.OnBluetoothStateChangeListener;\n\nimport java.nio.charset.StandardCharsets;\n\nimport cn.pedant.SweetAlert.SweetAlertDialog;\n\npublic class MainActivity extends AppCompatActivity implements OnBluetoothStateChangeListener, OnBluetoothConnectStateChangeListener {\n    private static final String TAG = \"MainActivity\";\n    private static final int QUEST_BLUETOOTH_DEVICE = 1;\n\n    private TextView appVersion;\n    private TextView bleStatus;\n    private TextView bleConnectStatus;\n    // 蓝牙相关\n    public static BluetoothDeviceBean mDeviceModel;\n    public BluetoothController mBluetoothController;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        initView();\n        initBluetooth();\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n    }\n\n    @Override\n    public void onDestroy() {\n        if (mBluetoothController != null) {\n            mBluetoothController.setProtocol(null);\n            mBluetoothController.unregisterBluetoothStateChangeListener();\n            mBluetoothController.unregisterConnectStateChangeListener();\n            mBluetoothController.disconnect();\n            mBluetoothController.stopScan();\n        }\n        DialogUtils.dismissDialog();\n        super.onDestroy();\n    }\n\n    private void initView() {\n        appVersion = findViewById(R.id.app_version);\n        bleStatus = findViewById(R.id.ble_status);\n        bleConnectStatus = findViewById(R.id.ble_connect_status);\n        appVersion.setText(\"\" + BuildConfig.VERSION_NAME + \" - \" + BuildConfig.VERSION_CODE);\n    }\n\n    /**\n     * 初始化蓝牙框架\n     */\n    private void initBluetooth() {\n        mBluetoothController = BluetoothController.getController(this);\n        mBluetoothController.registerBluetoothStateChangeListener(this);\n        mBluetoothController.registerConnectStateChangeListener(this);\n        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();\n        if (bluetoothAdapter == null) {\n            LogUtils.d(TAG + \"lpq\", \"initBluetooth: 该设备不支持蓝牙\");\n            ToastUtil.showShort(this, \"该设备不支持蓝牙！无法正常使用\");\n            return;\n        }\n        if (!bluetoothAdapter.isEnabled()) {\n            bleStatus.setText(\"已关闭\");\n            bluetoothAdapter.enable();\n        } else {\n            bleStatus.setText(\"已打开\");\n        }\n        /**\n         * 可以选择作为客户端搜索传统蓝牙，低功耗蓝牙\n         * 或者作为传统蓝牙服务端\n         */\n        mBluetoothController.setBluetoothType(BluetoothController.TYPE_BLE);\n    }\n\n    /**\n     * 搜索和连接蓝牙\n     *\n     * @param view\n     */\n    public void btnSearchBle(View view) {\n        if (!GpsUtils.isOPen(this)) {\n            DialogUtils.showNormalDialog(this, \"提示\", \"为保证初始化功能的正常使用，请开启定位功能\", new SweetAlertDialog.OnSweetClickListener() {\n                @Override\n                public void onClick(SweetAlertDialog sweetAlertDialog) {\n                    DialogUtils.dismissDialog();\n                    Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);\n                    startActivity(intent);\n                }\n            });\n            return;\n        }\n        if (mBluetoothController.isConnected()) {\n            mBluetoothController.disconnect();\n        } else {\n            Intent deviceIntent = new Intent(this, DeviceListActivity.class);\n            startActivityForResult(deviceIntent, QUEST_BLUETOOTH_DEVICE);\n        }\n    }\n\n    /**\n     * 使用mac地址连接蓝牙\n     *\n     * @param view\n     */\n    public void btnMacConnect(View view) {\n        mBluetoothController.connect(\"40:C8:1F:6A:CD:2B\", new CustomProtocol(this, null));\n    }\n\n    /**\n     * 断开蓝牙\n     */\n    public void btnDisconnectBle(View view) {\n        LogUtils.d(TAG + \"lpq\", \"btnDisconnectBle: \");\n        if (mBluetoothController.isConnected()) {\n            mBluetoothController.disconnect();\n        } else {\n            mBluetoothController.stopScan();\n        }\n    }\n\n    /**\n     * 蓝牙搜索结果\n     *\n     * @param requestCode\n     * @param resultCode\n     * @param data\n     */\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, final Intent data) {\n        if (resultCode != RESULT_OK) {\n            return;\n        }\n        mDeviceModel = (BluetoothDeviceBean) data.getSerializableExtra(DeviceListActivity.KEY_SCAN_DEVICE);\n        LogUtils.d(TAG + \"lpq\", \"onActivityResult: \" + mDeviceModel.toString());\n        String deviceName = mDeviceModel.getDeviceName();\n        if (deviceName != null) {\n            if (mDeviceModel.isBle()) {\n                mBluetoothController.setBluetoothType(BluetoothController.TYPE_BLE);\n            } else {\n                mBluetoothController.setBluetoothType(BluetoothController.TYPE_TRADITION);\n            }\n            mBluetoothController.setProtocol(new CustomProtocol(this, null));\n            mBluetoothController.connect(mDeviceModel.getDeviceAddress());\n        } else {\n            ToastUtil.showShort(this, \"蓝牙名称为空，请重试\");\n        }\n    }\n\n    /********************************* 蓝牙监听回调 *************************************/\n\n    @Override\n    public void onBluetoothOpen() {\n        LogUtils.d(TAG + \"lpq\", \"onBluetoothOpen: 蓝牙已打开\");\n        bleStatus.setText(\"已打开\");\n    }\n\n    @Override\n    public void onBluetoothClose() {\n        LogUtils.d(TAG + \"lpq\", \"onBluetoothClose: 蓝牙已关闭\");\n        bleStatus.setText(\"已关闭\");\n    }\n\n    /**\n     * 蓝牙连接事件回调\n     *\n     * @param device    蓝牙设备\n     * @param isSuccess 是否成功\n     */\n    @Override\n    public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {\n        LogUtils.d(TAG + \"lpq\", \"onBluetoothConnect: 蓝牙连接结果：\" + isSuccess);\n        if (isSuccess) {\n            bleConnectStatus.setText(\"已连接\");\n            mBluetoothController.setProtocol(new CustomProtocol(this, null));\n        } else {\n            bleConnectStatus.setText(\"未连接\");\n        }\n    }\n\n    /**\n     * 蓝牙断开连接事件回调\n     *\n     * @param device 蓝牙设备\n     */\n    @Override\n    public void onBluetoothDisconnect(BluetoothDevice device) {\n        bleConnectStatus.setText(\"未连接\");\n    }\n\n    /**\n     * 协议解析测试\n     *\n     * @param view\n     */\n    public void btnProtocolParse(View view) {\n        LogUtils.d(TAG + \"lpq\", \"btnProtocolParse: 蓝牙协议解析测试\");\n        CustomProtocol customProtocol = new CustomProtocol(this, null);\n        String a1String = \"02413130313139323032303\";\n        byte[] bytes = ByteUtils.hexStringToBytes(a1String);\n        LogUtils.d(TAG + \"lpq\", \"btnProtocolParse: bytes = \" + ByteUtils.toString(bytes, \"\"));\n        customProtocol.parse(bytes);\n    }\n\n    /**\n     * 协议打包测试\n     *\n     * @param view\n     */\n    public void btnProtocolPacket(View view) {\n        LogUtils.d(TAG + \"lpq\", \"btnProtocolParse: 蓝牙协议解析测试\");\n        CustomProtocol customProtocol = new CustomProtocol(this, null);\n        byte[] sendBytes = \"00\".getBytes(StandardCharsets.US_ASCII);\n        LogUtils.d(TAG + \"lpq\", \"confirm: sendBytes = \" + ByteUtils.toString(sendBytes));\n        new CustomProtocol(this, null).packetToBytes(new CustomPacket(0x01 /* 命令码 */, sendBytes, WeiCeCode.EXPAND_CODE_READ));\n    }\n\n    /**\n     * 发送简单数据\n     * 这个不会走封装好的协议\n     *\n     * @param view\n     */\n    public void btnSendConfirmData(View view) {\n        WeiCeDeviceOperate.getSn(new OnTimeoutResult<String>() {\n            @Override\n            public void onResult(boolean isTimeout, String result) {\n                Log.i(\"lpq\", \"onResult: isTimeout = \" + isTimeout);\n                Log.i(\"lpq\", \"onResult: sn = \" + result);\n            }\n        }, true);\n//        byte[] bytes = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06};\n//        mBluetoothController.sendData(bytes);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/MyApplication.java",
    "content": "package com.luo.bluetooth;\n\nimport android.app.Application;\n\nimport com.tencent.bugly.Bugly;\n\npublic class MyApplication extends Application {\n    private static final String TAG = \"MyApplication\";\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Bugly.init(getApplicationContext(), \"4efc7ff5d2\", false);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/base/BaseActivity.java",
    "content": "package com.luo.bluetooth.base;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\n\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport java.lang.ref.WeakReference;\n\npublic class BaseActivity extends AppCompatActivity {\n\n    //全局变量\n    protected static boolean isConnecting = false;\n    protected static boolean isLockConnecting = false;\n\n    public static WeakReference<Context> currentResumeContext;\n    protected Context mContext;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mContext = this;\n    }\n\n\n    @Override\n    protected void onResume() {\n        currentResumeContext = new WeakReference<Context>(this);\n        super.onResume();\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n    }\n\n    public void startToActivity(Class activityClass) {\n        Intent intent = new Intent(this, activityClass);\n        startActivity(intent);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/common/Constants.java",
    "content": "package com.luo.bluetooth.common;\n\npublic class Constants {\n\n    /**\n     * 蓝牙相关\n     */\n    //蓝牙连接状态\n    public static final int BLUETOOTH_STATUS_INIT = 0;//蓝牙钥匙状态，未连接过\n    public static final int BLUETOOTH_STATUS_CONNECTED = 1;//蓝牙钥匙状态，连接过\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/BluetoothDeviceAdapter.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.TextView;\n\nimport com.luo.bluetooth.R;\nimport com.luo.bluetooth.common.Constants;\n\nimport java.util.List;\n\n/**\n * 蓝牙搜索界面适配器\n * Created by Administrator on 2016/7/23 0023.\n */\npublic class BluetoothDeviceAdapter extends BaseAdapter{\n    private Context mContext;\n    private List<BluetoothDeviceBean> mModels;\n\n    public BluetoothDeviceAdapter(Context mContext, List<BluetoothDeviceBean> mModels) {\n        this.mContext = mContext;\n        this.mModels = mModels;\n    }\n\n    @Override\n    public int getCount() {\n        return mModels.size();\n    }\n\n    @Override\n    public Object getItem(int i) {\n        return mModels.get(i);\n    }\n\n    @Override\n    public long getItemId(int i) {\n        return i;\n    }\n\n    @Override\n    public View getView(int i, View view, ViewGroup viewGroup) {\n        ViewHolder holder=null;\n        if(view==null){\n            view= LayoutInflater.from(mContext).inflate(R.layout.item_bluetooth_device_list,null);\n            holder=new ViewHolder();\n            holder.tvName= (TextView) view.findViewById(R.id.tv_bluetooth_name);\n            holder.tvAddress= (TextView) view.findViewById(R.id.tv_bluetooth_address);\n            holder.svBluetooth= (SignalView) view.findViewById(R.id.svBluetooth);\n            view.setTag(holder);\n        }else{\n            holder= (ViewHolder) view.getTag();\n        }\n        initView(i,holder);\n        return view;\n    }\n    public void initView(int i,ViewHolder holder){\n        BluetoothDeviceBean device=mModels.get(i);\n        StringBuffer buf=new StringBuffer();\n        buf.append(device.getDeviceName());\n        if(!TextUtils.isEmpty(device.getNickName())){\n            buf.append(\"【\"+device.getNickName()+\"】\");\n        }\n        holder.tvName.setText(buf.toString());\n        if(device.getStatus()== Constants.BLUETOOTH_STATUS_CONNECTED){\n            holder.tvAddress.setTextColor(Color.BLUE);\n        }else{\n            holder.tvAddress.setTextColor(Color.RED);\n        }\n        holder.tvAddress.setText(device.getDeviceAddress());\n        int intensity = (device.getRssi()+100)/12;\n        holder.svBluetooth.setIntensity(intensity);\n//        Log.d(\"LPQ3\", \"name:\"+holder.tvAddress.getText()+\" rssi:\"+device.getRssi()+\" intensity:\"+intensity);\n    }\n    private class ViewHolder{\n        public TextView tvName;\n        public TextView tvAddress;\n        public SignalView svBluetooth;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/BluetoothDeviceBean.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport java.io.Serializable;\n\nimport lombok.Data;\n\n/**\n * Created by Administrator on 2016/7/23 0023.\n */\n@Data\npublic class BluetoothDeviceBean implements Serializable {\n    private String deviceName;\n    private String deviceAddress;\n    private int status;\n    private boolean isBle;\n    private String nickName;\n    private int rssi;\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/ColorUtils.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport android.graphics.ColorFilter;\nimport android.graphics.ColorMatrixColorFilter;\n\n// http://stackoverflow.com/a/11171509/317862\npublic class ColorUtils {\n\n    public static ColorFilter getColorFilter(int color) {\n        ColorMatrixColorFilter colorFilter;\n        int red = (color & 0xFF0000) / 0xFFFF;\n        int green = (color & 0xFF00) / 0xFF;\n        int blue = color & 0xFF;\n\n        float[] matrix = { 0, 0, 0, 0, red\n                , 0, 0, 0, 0, green\n                , 0, 0, 0, 0, blue\n                , 0, 0, 0, 1, 0 };\n\n        colorFilter = new ColorMatrixColorFilter(matrix);\n\n        return colorFilter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/DeviceListActivity.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport android.Manifest;\nimport android.bluetooth.BluetoothDevice;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\n\nimport androidx.core.app.ActivityCompat;\nimport androidx.core.content.ContextCompat;\n\nimport com.stag.bluetooth.BluetoothController;\nimport com.stag.bluetooth.OnBluetoothScanListener;\nimport com.luo.bluetooth.R;\nimport com.luo.bluetooth.base.BaseActivity;\nimport com.luo.bluetooth.common.Constants;\nimport com.luo.bluetooth.utils.DialogUtils;\nimport com.luo.bluetooth.utils.LogUtils;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport cn.pedant.SweetAlert.SweetAlertDialog;\n\nimport static android.content.pm.PackageManager.PERMISSION_GRANTED;\n\n/**\n * 搜索蓝牙设备界面\n * Created by Administrator on 2016/7/22 0022.\n */\npublic class DeviceListActivity extends BaseActivity implements View.OnClickListener, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, OnBluetoothScanListener {\n    private static final String TAG = \"DeviceListActivity\";\n    public static final String KEY_SCAN_DEVICE = \"KEY_SCAN_DEVICE\";\n    private Button btnSearch, btnHistory, btnCancel;\n    private ListView lvDevices;\n    private TextView tvTitle, tvNoKey;\n    private ProgressBar pbScanning;\n    private BluetoothDeviceAdapter adapter;\n    private List<BluetoothDeviceBean> mDeviceModes = new LinkedList<BluetoothDeviceBean>();\n    private BluetoothController mBluetoothController;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        LogUtils.d(TAG + \": lpq\", \"onCreate: \");\n        setContentView(R.layout.activity_search_device_list);\n        findViews();\n        initData();\n        bindEvent();\n        this.setFinishOnTouchOutside(false);\n    }\n\n    @Override\n    public void finish() {\n        LogUtils.d(TAG + \": lpq\", \"finish: \");\n        super.finish();\n        overridePendingTransition(R.anim.fade_in, R.anim.fade_out);\n    }\n\n    @Override\n    protected void onDestroy() {\n        LogUtils.d(TAG + \": lpq\", \"onDestroy: \");\n        mBluetoothController.stopScan();\n        super.onDestroy();\n    }\n\n    protected void findViews() {\n        lvDevices = (ListView) findViewById(R.id.lv_devices);\n        tvTitle = (TextView) findViewById(R.id.tvTitle);\n        tvNoKey = (TextView) findViewById(R.id.tv_no_key);\n        pbScanning = (ProgressBar) findViewById(R.id.pbScanning);\n        btnSearch = (Button) findViewById(R.id.btn_search);\n        btnHistory = (Button) findViewById(R.id.btn_history);\n        btnCancel = (Button) findViewById(R.id.btn_cancel);\n    }\n\n    public void initData() {\n        mBluetoothController = BluetoothController.getController(this);\n        tvTitle.setText(\"设备列表\");\n        lvDevices.setEmptyView(tvNoKey);\n        adapter = new BluetoothDeviceAdapter(this, mDeviceModes);\n        lvDevices.setAdapter(adapter);\n        startSearchKey();\n        getPermissions();\n    }\n\n    /**\n     * 获取权限\n     */\n    private void getPermissions() {\n        if (ContextCompat.checkSelfPermission(DeviceListActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {\n            //请求权限\n            ActivityCompat.requestPermissions(DeviceListActivity.this,\n                    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1);\n            //判断是否需要 向用户解释，为什么要申请该权限\n            ActivityCompat.shouldShowRequestPermissionRationale(DeviceListActivity.this,\n                    Manifest.permission.READ_CONTACTS);\n        }\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {\n        if (grantResults != null && grantResults.length > 0) {\n            if (grantResults[0] == PERMISSION_GRANTED) {\n                startSearchKey();\n            } else {\n                DialogUtils.showErrorDialog(mContext, \"没有权限无法使用蓝牙\", new SweetAlertDialog.OnSweetClickListener() {\n                    @Override\n                    public void onClick(SweetAlertDialog sweetAlertDialog) {\n                        DialogUtils.dismissDialog();\n                        finish();\n                    }\n                });\n            }\n        }\n    }\n\n    private void startSearchKey() {\n        LogUtils.d(TAG + \": lpq\", \"startSearchKey: \");\n        tvTitle.setText(R.string.title_device_list_search_key);\n        showProgressBar();\n\n        mDeviceModes.clear();\n        adapter.notifyDataSetChanged();\n        mBluetoothController.startScan(this);\n    }\n\n    private void stopSearchKey() {\n        LogUtils.d(TAG + \": lpq\", \"stopSearchKey: \");\n        tvTitle.setText(R.string.title_device_list_stop_search);\n        hideProgressBar();\n        mBluetoothController.stopScan();\n    }\n\n    private void showHistory() {\n        stopSearchKey();\n        tvTitle.setText(R.string.title_device_list_history);\n        hideProgressBar();\n        mDeviceModes.clear();\n        adapter.notifyDataSetChanged();\n    }\n\n    protected void bindEvent() {\n        btnSearch.setOnClickListener(this);\n        btnCancel.setOnClickListener(this);\n        btnHistory.setOnClickListener(this);\n        lvDevices.setOnItemClickListener(this);\n        lvDevices.setOnItemLongClickListener(this);\n    }\n\n    @Override\n    public void onClick(View view) {\n        switch (view.getId()) {\n            case R.id.btn_search:\n                startSearchKey();\n                break;\n            case R.id.btn_history:\n                showHistory();\n                break;\n            case R.id.btn_cancel:\n                stopSearchKey();\n                finish();\n                break;\n            default:\n                break;\n        }\n    }\n\n    @Override\n    public void onItemClick(AdapterView<?> adapterView, View view, final int i, long l) {\n        BluetoothDeviceBean model = mDeviceModes.get(i);\n        model.setStatus(Constants.BLUETOOTH_STATUS_CONNECTED);\n        //连接成功之后再保存会好一点\n//        model.saveOrUpdate(\"deviceAddress = ?\", model.getDeviceAddress());\n        Intent intent = new Intent();\n        intent.putExtra(KEY_SCAN_DEVICE, model);\n        setResult(RESULT_OK, intent);\n        stopSearchKey();\n        finish();\n    }\n\n    @Override\n    public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {\n        showSetNickNameDialog(this, mDeviceModes.get(i));\n        return true;\n    }\n\n    public void showSetNickNameDialog(Context context, final BluetoothDeviceBean model) {\n        final EditText editText = new EditText(this);\n        editText.setText(model.getNickName());\n        final NiftyDialogBuilder dialogBuilder = NiftyDialogBuilder.getInstance(context);\n        dialogBuilder.withTitle(getResources().getString(R.string.dialog_set_note_name))//.withTitle(null)  no title\n                .withMessage(null)\n                .withEffect(Effectstype.values()[9])\n                .withTitleColor(\"#FFFFFF\")                                  //def\n                .withDividerColor(\"#11000000\")                              //def\n                .withMessageColor(\"#FFFFFFFF\")                              //def  | withMessageColor(int resid)\n                .withDialogColor(getResources().getColor(R.color.colorPrimary))                               //def  | withDialogColor(int resid)\n//                .withIcon(context.getResources().getDrawable(R.mipmap.logo))\n                .withDuration(700)                                          //def\n                //   .withEffect(effect)                                         //def Effectstype.Slidetop\n                .withButton1Text(context.getString(R.string.btn_ok))                                    //def gone\n                .isCancelableOnTouchOutside(false)                           //def    | isCancelable(true)\n                //  .setCustomView(R.layout.custom_view,v.getContext())         //.setCustomView(View or ResId,context)\n                .setCustomView(editText, this)\n                .setButton1Click(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        model.setNickName(editText.getText().toString());\n//                        model.saveOrUpdate(\"deviceAddress = ?\", model.getDeviceAddress());\n                        adapter.notifyDataSetChanged();\n                        dialogBuilder.dismiss();\n                    }\n                }).withButton2Text(context.getString(R.string.btn_cancel))\n                .setButton2Click(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View view) {\n                        dialogBuilder.dismiss();\n                    }\n                })\n                .show();\n    }\n\n    @Override\n    public void onBluetoothScanFindDevice(BluetoothDevice device, int rssi, boolean isBle) {\n        for (BluetoothDeviceBean m : mDeviceModes) {\n            if (m.getDeviceAddress().equals(device.getAddress())) {\n                return;\n            }\n        }\n        Log.i(\"lpq\", \"onBluetoothScanFindDevice: device.name = \" + device.getName() + \"device.mac = \" + device.getAddress());\n//        List<BluetoothDeviceBean> deviceBeans = LitePal.where(\"deviceAddress = ?\", device.getAddress()).find(BluetoothDeviceBean.class);\n//        BluetoothDeviceBean model = deviceBeans.size() == 0 ? null : deviceBeans.get(0);\n        BluetoothDeviceBean model = null;\n        if (model == null) {\n            model = new BluetoothDeviceBean();\n            model.setDeviceAddress(device.getAddress());\n            model.setDeviceName(device.getName());\n            model.setStatus(Constants.BLUETOOTH_STATUS_INIT);\n            model.setBle(isBle);\n        } else {\n            model.setDeviceName(device.getName());\n            //感觉这句话没有什么用\n//            model.saveOrUpdate(\"deviceAddress = ?\", model.getDeviceAddress());\n//            BluetoothDeviceUtil.saveBluetoothDevice(mContext, model);\n        }\n        model.setRssi(rssi);\n        int index = -1;\n        for (int i=0;i<mDeviceModes.size();i++){\n            if (mDeviceModes.get(i).getRssi() < rssi) {\n                index = i;\n                break;\n            }\n        }\n        if (index==-1)\n            mDeviceModes.add(model);\n        else\n            mDeviceModes.add(index, model);\n        adapter.notifyDataSetChanged();\n    }\n\n    private void showProgressBar(){\n        pbScanning.setVisibility(View.VISIBLE);\n    }\n\n    private void hideProgressBar(){\n        pbScanning.setVisibility(View.GONE);\n    }\n\n    @Override\n    public void onBluetoothScanFinish() {\n        tvTitle.setText(R.string.title_device_list_select_key);\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/Effectstype.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\n\nimport com.luo.bluetooth.customview.searchble.effects.BaseEffects;\nimport com.luo.bluetooth.customview.searchble.effects.FadeIn;\nimport com.luo.bluetooth.customview.searchble.effects.Fall;\nimport com.luo.bluetooth.customview.searchble.effects.FlipH;\nimport com.luo.bluetooth.customview.searchble.effects.FlipV;\nimport com.luo.bluetooth.customview.searchble.effects.NewsPaper;\nimport com.luo.bluetooth.customview.searchble.effects.RotateBottom;\nimport com.luo.bluetooth.customview.searchble.effects.RotateLeft;\nimport com.luo.bluetooth.customview.searchble.effects.Shake;\nimport com.luo.bluetooth.customview.searchble.effects.SideFall;\nimport com.luo.bluetooth.customview.searchble.effects.SlideBottom;\nimport com.luo.bluetooth.customview.searchble.effects.SlideLeft;\nimport com.luo.bluetooth.customview.searchble.effects.SlideRight;\nimport com.luo.bluetooth.customview.searchble.effects.SlideTop;\nimport com.luo.bluetooth.customview.searchble.effects.Slit;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic enum Effectstype {\n\n    fadein(FadeIn.class),\n    slideleft(SlideLeft.class),\n    slidetop(SlideTop.class),\n    slidebottom(SlideBottom.class),\n    slideright(SlideRight.class),\n    fall(Fall.class),\n    newspager(NewsPaper.class),\n    fliph(FlipH.class),\n    flipv(FlipV.class),\n    rotateBottom(RotateBottom.class),\n    rotateLeft(RotateLeft.class),\n    slit(Slit.class),\n    shake(Shake.class),\n    sidefill(SideFall.class);\n\n    private Class<? extends BaseEffects> effectsClazz;\n\n    private Effectstype(Class<? extends BaseEffects> mclass) {\n        effectsClazz = mclass;\n    }\n\n    public BaseEffects getAnimator() {\n        BaseEffects bEffects=null;\n\ttry {\n\t\tbEffects = effectsClazz.newInstance();\n\t} catch (ClassCastException e) {\n\t\tthrow new Error(\"Can not init animatorClazz instance\");\n\t} catch (InstantiationException e) {\n\t\t// TODO Auto-generated catch block\n\t\tthrow new Error(\"Can not init animatorClazz instance\");\n\t} catch (IllegalAccessException e) {\n\t\t// TODO Auto-generated catch block\n\t\tthrow new Error(\"Can not init animatorClazz instance\");\n\t}\n\treturn bEffects;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/NiftyDialogBuilder.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.graphics.Color;\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.Button;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.luo.bluetooth.R;\nimport com.luo.bluetooth.customview.searchble.effects.BaseEffects;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class NiftyDialogBuilder extends Dialog implements DialogInterface {\n    \n    private final String defTextColor=\"#FFFFFFFF\";\n    \n    private final String defDividerColor=\"#11000000\";\n    \n    private final String defMsgColor=\"#FFFFFFFF\";\n    \n    private final String defDialogColor=\"#FFE74C3C\";\n\n\n    private static Context tmpContext;\n    \n    \n    private Effectstype type=null;\n    \n    private LinearLayout mLinearLayoutView;\n    \n    private RelativeLayout mRelativeLayoutView;\n    \n    private LinearLayout mLinearLayoutMsgView;\n    \n    private LinearLayout mLinearLayoutTopView;\n    \n    private FrameLayout mFrameLayoutCustomView;\n    \n    private View mDialogView;\n    \n    private View mDivider;\n    \n    private TextView mTitle;\n    \n    private TextView mMessage;\n    \n    private ImageView mIcon;\n    \n    private Button mButton1;\n    \n    private Button mButton2;\n    \n    private int mDuration = -1;\n    \n    private static  int mOrientation=1;\n    \n    private boolean isCancelable=true;\n    \n    private static NiftyDialogBuilder instance;\n    \n    public NiftyDialogBuilder(Context context) {\n        super(context);\n        init(context);\n        \n    }\n    public NiftyDialogBuilder(Context context, int theme) {\n        super(context, theme);\n        init(context);\n    }\n    \n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        WindowManager.LayoutParams params = getWindow().getAttributes();\n        params.height = ViewGroup.LayoutParams.MATCH_PARENT;\n        params.width  = ViewGroup.LayoutParams.MATCH_PARENT;\n        getWindow().setAttributes((WindowManager.LayoutParams) params);\n        \n    }\n    \n    public static NiftyDialogBuilder getInstance(Context context) {\n\n        if (instance == null || !tmpContext.equals(context)) {\n            synchronized (NiftyDialogBuilder.class) {\n                if (instance == null || !tmpContext.equals(context)) {\n                    instance = new NiftyDialogBuilder(context,  R.style.dialog_untran);\n                }\n            }\n        }\n        tmpContext = context;\n        return instance;\n\n    }\n    \n    private void init(Context context) {\n        \n        mDialogView = View.inflate(context, R.layout.dialog_layout, null);\n        \n        mLinearLayoutView=(LinearLayout)mDialogView.findViewById(R.id.parentPanel);\n        mRelativeLayoutView=(RelativeLayout)mDialogView.findViewById(R.id.main);\n        mLinearLayoutTopView=(LinearLayout)mDialogView.findViewById(R.id.topPanel);\n        mLinearLayoutMsgView=(LinearLayout)mDialogView.findViewById(R.id.contentPanel);\n        mFrameLayoutCustomView=(FrameLayout)mDialogView.findViewById(R.id.customPanel);\n        \n        mTitle = (TextView) mDialogView.findViewById(R.id.alertTitle);\n        mMessage = (TextView) mDialogView.findViewById(R.id.message);\n        mIcon = (ImageView) mDialogView.findViewById(R.id.icon);\n        mDivider = mDialogView.findViewById(R.id.titleDivider);\n        mButton1=(Button)mDialogView.findViewById(R.id.button1);\n        mButton2=(Button)mDialogView.findViewById(R.id.button2);\n        \n        setContentView(mDialogView);\n        \n        this.setOnShowListener(new OnShowListener() {\n            @Override\n            public void onShow(DialogInterface dialogInterface) {\n                \n                mLinearLayoutView.setVisibility(View.VISIBLE);\n                if(type==null){\n                    type=Effectstype.slidetop;\n                }\n                start(type);\n                \n                \n            }\n        });\n        mRelativeLayoutView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                if (isCancelable)dismiss();\n            }\n        });\n    }\n    \n    public void toDefault(){\n        mTitle.setTextColor(Color.parseColor(defTextColor));\n        mDivider.setBackgroundColor(Color.parseColor(defDividerColor));\n        mMessage.setTextColor(Color.parseColor(defMsgColor));\n        mLinearLayoutView.setBackgroundColor(Color.parseColor(defDialogColor));\n    }\n    \n    public NiftyDialogBuilder withDividerColor(String colorString) {\n        mDivider.setBackgroundColor(Color.parseColor(colorString));\n        return this;\n    }\n    public NiftyDialogBuilder withDividerColor(int color) {\n        mDivider.setBackgroundColor(color);\n        return this;\n    }\n    \n    \n    public NiftyDialogBuilder withTitle(CharSequence title) {\n        toggleView(mLinearLayoutTopView,title);\n        mTitle.setText(title);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withTitleColor(String colorString) {\n        mTitle.setTextColor(Color.parseColor(colorString));\n        return this;\n    }\n    \n    public NiftyDialogBuilder withTitleColor(int color) {\n        mTitle.setTextColor(color);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withMessage(int textResId) {\n        toggleView(mLinearLayoutMsgView,textResId);\n        mMessage.setText(textResId);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withMessage(CharSequence msg) {\n        toggleView(mLinearLayoutMsgView,msg);\n        mMessage.setText(msg);\n        return this;\n    }\n    public NiftyDialogBuilder withMessageColor(String colorString) {\n        mMessage.setTextColor(Color.parseColor(colorString));\n        return this;\n    }\n    public NiftyDialogBuilder withMessageColor(int color) {\n        mMessage.setTextColor(color);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withDialogColor(String colorString) {\n        mLinearLayoutView.getBackground().setColorFilter(ColorUtils.getColorFilter(Color.parseColor(colorString)));\n        return this;\n    }\n    \n    public NiftyDialogBuilder withDialogColor(int color) {\n        mLinearLayoutView.getBackground().setColorFilter(ColorUtils.getColorFilter(color));\n        return this;\n    }\n    \n    public NiftyDialogBuilder withIcon(int drawableResId) {\n        mIcon.setImageResource(drawableResId);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withIcon(Drawable icon) {\n        mIcon.setImageDrawable(icon);\n        return this;\n    }\n    \n    public NiftyDialogBuilder withDuration(int duration) {\n        this.mDuration=duration;\n        return this;\n    }\n    \n    public NiftyDialogBuilder withEffect(Effectstype type) {\n        this.type=type;\n        return this;\n    }\n    \n    public NiftyDialogBuilder withButtonDrawable(int resid) {\n        mButton1.setBackgroundResource(resid);\n        mButton2.setBackgroundResource(resid);\n        return this;\n    }\n    public NiftyDialogBuilder withButton1Text(CharSequence text) {\n        mButton1.setVisibility(View.VISIBLE);\n        mButton1.setText(text);\n        \n        return this;\n    }\n    public NiftyDialogBuilder withButton2Text(CharSequence text) {\n        mButton2.setVisibility(View.VISIBLE);\n        mButton2.setText(text);\n        return this;\n    }\n    public NiftyDialogBuilder setButton1Click(View.OnClickListener click) {\n        mButton1.setOnClickListener(click);\n        return this;\n    }\n    \n    public NiftyDialogBuilder setButton2Click(View.OnClickListener click) {\n        mButton2.setOnClickListener(click);\n        return this;\n    }\n    \n    \n    public NiftyDialogBuilder setCustomView(int resId, Context context) {\n        View customView = View.inflate(context, resId, null);\n        if (mFrameLayoutCustomView.getChildCount()>0){\n            mFrameLayoutCustomView.removeAllViews();\n        }\n        mFrameLayoutCustomView.addView(customView);\n        return this;\n    }\n    \n    public NiftyDialogBuilder setCustomView(View view, Context context) {\n        if (mFrameLayoutCustomView.getChildCount()>0){\n            mFrameLayoutCustomView.removeAllViews();\n        }\n        mFrameLayoutCustomView.addView(view);\n        \n        return this;\n    }\n\n    public NiftyDialogBuilder removeAllCustomView(){\n        if (mFrameLayoutCustomView.getChildCount()>0){\n            mFrameLayoutCustomView.removeAllViews();\n        }\n        return this;\n    }\n\n\n    public NiftyDialogBuilder isCancelableOnTouchOutside(boolean cancelable) {\n        this.isCancelable=cancelable;\n        this.setCanceledOnTouchOutside(cancelable);\n        return this;\n    }\n    \n    public NiftyDialogBuilder isCancelable(boolean cancelable) {\n        this.isCancelable=cancelable;\n        this.setCancelable(cancelable);\n        return this;\n    }\n    \n    private void toggleView(View view, Object obj){\n        if (obj==null){\n            view.setVisibility(View.GONE);\n        }else {\n            view.setVisibility(View.VISIBLE);\n        }\n    }\n    @Override\n    public void show() {\n        super.show();\n    }\n    \n    private void start(Effectstype type){\n        BaseEffects animator = type.getAnimator();\n        if(mDuration != -1){\n            animator.setDuration(Math.abs(mDuration));\n        }\n        animator.start(mRelativeLayoutView);\n    }\n    \n    @Override\n    public void dismiss() {\n        super.dismiss();\n        mButton1.setVisibility(View.GONE);\n        mButton2.setVisibility(View.GONE);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/SignalView.java",
    "content": "package com.luo.bluetooth.customview.searchble;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.luo.bluetooth.R;\n\n\n/**\n * 强度指示器，等级0-4\n * Created by LPQ on 2017/1/17.\n */\n\npublic class SignalView extends View {\n\n    private Paint mPaint;\n    private int mWidth, mHeight;\n    private float cx, cy, radius;\n    private int mNoIntensityColor, mIntensityColor;\n    private int mIntensity;\n\n    public SignalView(Context context) {\n        this(context, null);\n    }\n\n    public SignalView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SignalView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignalView, defStyleAttr, 0);\n        mIntensityColor = typedArray.getColor(R.styleable.SignalView_intensityColor, 0xff6cb1f2);\n        mNoIntensityColor = typedArray.getColor(R.styleable.SignalView_noIntensityColor, 0xffbdc3c7);\n        typedArray.recycle();\n        init();\n    }\n\n    private void init(){\n//        setWillNotDraw(false);\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setStrokeCap(Paint.Cap.ROUND);\n    }\n\n    /**\n     * 设置强度\n     * */\n    public void setIntensity(int intensity){\n        mIntensity = intensity;\n        if (intensity>4)\n            mIntensity = 4;\n        if (intensity<0)\n            mIntensity = 0;\n        invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        mWidth = getMeasuredWidth();\n        mHeight = getMeasuredHeight();\n        cx = mWidth/2.f;\n        cy = mHeight*0.7f;\n        if (mWidth>mHeight){\n            radius = mHeight/12.f;\n        }else {\n            radius = mWidth/12.f;\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        mPaint.setStyle(Paint.Style.FILL);\n        mPaint.setColor(mIntensity>0?mIntensityColor:mNoIntensityColor);\n        canvas.drawCircle(cx, cy, radius, mPaint);\n        RectF rectF = new RectF(cx-2.5f*radius, cy-2.f*radius, cx+2.5f*radius, cy+4*radius);\n        mPaint.setStyle(Paint.Style.STROKE);\n        mPaint.setStrokeWidth(radius*0.8f);\n        mPaint.setColor(mIntensity>1?mIntensityColor:mNoIntensityColor);\n        canvas.drawArc(rectF, 220, 100, false, mPaint);\n        float offest = 1.5f*radius;\n        for (int i=2;i<4;i++){\n            mPaint.setColor(mIntensity>i?mIntensityColor:mNoIntensityColor);\n            rectF.left -= offest;\n            rectF.top -= offest;\n            rectF.right += offest;\n            canvas.drawArc(rectF, 220, 100, false, mPaint);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/BaseEffects.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.AnimatorSet;\nimport android.os.Build;\nimport android.view.View;\n\n\n/**\n * Modify by lee on 2014/7/30.\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * The MIT License (MIT)\n *\n * Copyright (c) 2014 daimajia\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n *\n * Acknowledgements daimajia\n * https://github.com/daimajia\n */\npublic abstract  class BaseEffects {\n\n    private static final int DURATION = 1 * 700;\n\n    protected long mDuration =DURATION ;\n\n    private AnimatorSet mAnimatorSet;\n\n    {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            mAnimatorSet = new AnimatorSet();\n        }\n    }\n\n    protected abstract void setupAnimation(View view);\n\n    public void start(View view) {\n        reset(view);\n        setupAnimation(view);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            mAnimatorSet.start();\n        }\n    }\n    public void reset(View view) {\n//        ViewHelper.setPivotX(view, view.getMeasuredWidth() / 2.0f);\n//        ViewHelper.setPivotY(view, view.getMeasuredHeight() / 2.0f);\n    }\n\n\n    public AnimatorSet getAnimatorSet() {\n        return mAnimatorSet;\n    }\n    \n    public void setDuration(long duration) {\n        this.mDuration = duration;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FadeIn.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class FadeIn extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Fall.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class Fall extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"scaleX\", 2, 1.5f, 1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view,\"scaleY\",2,1.5f,1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FlipH.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class FlipH  extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotationY\", -90, 0).setDuration(mDuration)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/FlipV.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class FlipV extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotationX\", -90, 0).setDuration(mDuration)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/NewsPaper.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class NewsPaper extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotation\", 1080, 720, 360, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2),\n                ObjectAnimator.ofFloat(view, \"scaleX\", 0.1f, 0.5f, 1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view,\"scaleY\",0.1f,0.5f,1).setDuration(mDuration)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/RotateBottom.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class RotateBottom extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotationX\",90, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"translationY\", 300, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/RotateLeft.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class RotateLeft extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotationY\", 90, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"translationX\", -300, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Shake.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.os.Build;\nimport android.view.View;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class Shake  extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            getAnimatorSet().playTogether(\n                    ObjectAnimator.ofFloat(view, \"translationX\", 0, .10f, -25, .26f, 25, .42f, -25, .58f, 25, .74f, -25, .90f, 1, 0).setDuration(mDuration)\n\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SideFall.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class SideFall extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"scaleX\", 2, 1.5f, 1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view,\"scaleY\",2,1.5f,1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"rotation\", 25,0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"translationX\",80,0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideBottom.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.os.Build;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class SlideBottom extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            getAnimatorSet().playTogether(\n                    ObjectAnimator.ofFloat(view, \"translationY\", 300, 0).setDuration(mDuration),\n                    ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideLeft.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class SlideLeft extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"translationX\", -300, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideRight.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class SlideRight extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"translationX\", 300, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/SlideTop.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class SlideTop extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"translationY\", -300, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0, 1).setDuration(mDuration*3/2)\n\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/customview/searchble/effects/Slit.java",
    "content": "package com.luo.bluetooth.customview.searchble.effects;\n\nimport android.animation.ObjectAnimator;\nimport android.view.View;\n\n\n/*\n * Copyright 2014 litao\n * https://github.com/sd6352051/NiftyDialogEffects\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class Slit extends BaseEffects{\n\n    @Override\n    protected void setupAnimation(View view) {\n        getAnimatorSet().playTogether(\n                ObjectAnimator.ofFloat(view, \"rotationY\", 90, 88, 88, 45, 0).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view, \"alpha\", 0,0.4f,0.8f, 1).setDuration(mDuration*3/2),\n                ObjectAnimator.ofFloat(view, \"scaleX\", 0,0.5f, 0.9f, 0.9f, 1).setDuration(mDuration),\n                ObjectAnimator.ofFloat(view,\"scaleY\",0,0.5f, 0.9f, 0.9f, 1).setDuration(mDuration)\n        );\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/encryption/Aes.java",
    "content": "/*\n * Copyright (c) 2019. stag All rights reserved.\n */\n\npackage com.luo.bluetooth.encryption;\n\n\nimport com.stag.bluetooth.util.ByteUtils;\n\n/**\n * AES加密\n */\npublic class Aes {\n\n    // foreward sbox\n    private static final byte sbox[] = {\n            //0          1            2           3           4            5           6            7           8            9            A           B            C           D           E            F\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n            (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\n    // inverse sbox\n    private static final byte rsbox[] = {\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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,\n            (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};\n    // round constant\n    private static final byte Rcon[] = {\n            (byte) 0x8d, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x08, (byte) 0x10, (byte) 0x20, (byte) 0x40, (byte) 0x80, (byte) 0x1b, (byte) 0x36};\n\n    // expand the key\n    private static byte[] expandKey(byte[] key) {\n        byte[] expandedKey = new byte[176];\n        int ii, buf1;\n        for (ii = 0; ii < 16; ii++)\n            expandedKey[ii] = key[ii];\n        for (ii = 1; ii < 11; ii++) {\n            buf1 = expandedKey[ii * 16 - 4];\n            expandedKey[ii * 16 + 0] = (byte) ((sbox[expandedKey[ii * 16 - 3] & 0xff] ^ expandedKey[(ii - 1) * 16 + 0] ^ Rcon[ii]) & 0xff);\n            expandedKey[ii * 16 + 1] = (byte) ((sbox[expandedKey[ii * 16 - 2] & 0xff] ^ expandedKey[(ii - 1) * 16 + 1]) & 0xff);\n            expandedKey[ii * 16 + 2] = (byte) ((sbox[expandedKey[ii * 16 - 1] & 0xff] ^ expandedKey[(ii - 1) * 16 + 2]) & 0xff);\n            expandedKey[ii * 16 + 3] = (byte) ((sbox[buf1 & 0xff] ^ expandedKey[(ii - 1) * 16 + 3]) & 0xff);\n            expandedKey[ii * 16 + 4] = (byte) ((expandedKey[(ii - 1) * 16 + 4] ^ expandedKey[ii * 16 + 0]) & 0xff);\n            expandedKey[ii * 16 + 5] = (byte) ((expandedKey[(ii - 1) * 16 + 5] ^ expandedKey[ii * 16 + 1]) & 0xff);\n            expandedKey[ii * 16 + 6] = (byte) ((expandedKey[(ii - 1) * 16 + 6] ^ expandedKey[ii * 16 + 2]) & 0xff);\n            expandedKey[ii * 16 + 7] = (byte) ((expandedKey[(ii - 1) * 16 + 7] ^ expandedKey[ii * 16 + 3]) & 0xff);\n            expandedKey[ii * 16 + 8] = (byte) ((expandedKey[(ii - 1) * 16 + 8] ^ expandedKey[ii * 16 + 4]) & 0xff);\n            expandedKey[ii * 16 + 9] = (byte) ((expandedKey[(ii - 1) * 16 + 9] ^ expandedKey[ii * 16 + 5]) & 0xff);\n            expandedKey[ii * 16 + 10] = (byte) ((expandedKey[(ii - 1) * 16 + 10] ^ expandedKey[ii * 16 + 6]) & 0xff);\n            expandedKey[ii * 16 + 11] = (byte) ((expandedKey[(ii - 1) * 16 + 11] ^ expandedKey[ii * 16 + 7]) & 0xff);\n            expandedKey[ii * 16 + 12] = (byte) ((expandedKey[(ii - 1) * 16 + 12] ^ expandedKey[ii * 16 + 8]) & 0xff);\n            expandedKey[ii * 16 + 13] = (byte) ((expandedKey[(ii - 1) * 16 + 13] ^ expandedKey[ii * 16 + 9]) & 0xff);\n            expandedKey[ii * 16 + 14] = (byte) ((expandedKey[(ii - 1) * 16 + 14] ^ expandedKey[ii * 16 + 10]) & 0xff);\n            expandedKey[ii * 16 + 15] = (byte) ((expandedKey[(ii - 1) * 16 + 15] ^ expandedKey[ii * 16 + 11]) & 0xff);\n        }\n        return expandedKey;\n    }\n\n    // multiply by 2 in the galois field\n    private static byte galois_mul2(byte value) {\n        if (value >> 7 != 0x00) {\n            value = (byte) ((value << 1) & 0xff);\n            return (byte) ((value ^ 0x1b) & 0xff);\n        } else {\n            return (byte) ((value << 1) & 0xff);\n        }\n    }\n\n    // straight foreward aes encryption implementation\n//   first the group of operations\n//     - addroundkey\n//     - subbytes\n//     - shiftrows\n//     - mixcolums\n//   is executed 9 times, after this addroundkey to finish the 9th round,\n//   after that the 10th round without mixcolums\n//   no further subfunctions to save cycles for function calls\n//   no structuring with \"for (....)\" to save cycles\n    private static byte[] aes_encr(byte[] input, byte[] expandedKey) {\n        byte buf1, buf2, buf3, round;\n        byte[] state = new byte[input.length];\n        for (int i = 0; i < input.length; i++) {\n            state[i] = input[i];\n        }\n        for (round = 0; round < 9; round++) {\n            // addroundkey, sbox and shiftrows\n            // row 0\n            state[0] = sbox[(state[0] ^ expandedKey[(round * 16)]) & 0xff];\n            state[4] = sbox[(state[4] ^ expandedKey[(round * 16) + 4]) & 0xff];\n            state[8] = sbox[(state[8] ^ expandedKey[(round * 16) + 8]) & 0xff];\n            state[12] = sbox[(state[12] ^ expandedKey[(round * 16) + 12]) & 0xff];\n            // row 1\n            buf1 = (byte) ((state[1] ^ expandedKey[(round * 16) + 1]) & 0xff);\n            state[1] = sbox[(state[5] ^ expandedKey[(round * 16) + 5]) & 0xff];\n            state[5] = sbox[(state[9] ^ expandedKey[(round * 16) + 9]) & 0xff];\n            state[9] = sbox[(state[13] ^ expandedKey[(round * 16) + 13]) & 0xff];\n            state[13] = sbox[buf1 & 0xff];\n            // row 2\n            buf1 = (byte) ((state[2] ^ expandedKey[(round * 16) + 2]) & 0xff);\n            buf2 = (byte) ((state[6] ^ expandedKey[(round * 16) + 6]) & 0xff);\n            state[2] = sbox[(state[10] ^ expandedKey[(round * 16) + 10]) & 0xff];\n            state[6] = sbox[(state[14] ^ expandedKey[(round * 16) + 14]) & 0xff];\n            state[10] = sbox[buf1 & 0xff];\n            state[14] = sbox[buf2 & 0xff];\n            // row 3\n            buf1 = (byte) ((state[15] ^ expandedKey[(round * 16) + 15]) & 0xff);\n            state[15] = sbox[(state[11] ^ expandedKey[(round * 16) + 11]) & 0xff];\n            state[11] = sbox[(state[7] ^ expandedKey[(round * 16) + 7]) & 0xff];\n            state[7] = sbox[(state[3] ^ expandedKey[(round * 16) + 3]) & 0xff];\n            state[3] = sbox[buf1 & 0xff];\n            // mixcolums //////////\n            // col1\n            buf1 = (byte) ((state[0] ^ state[1] ^ state[2] ^ state[3]) & 0xff);\n            buf2 = state[0];\n            buf3 = (byte) ((state[0] ^ state[1]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[0] = (byte) ((state[0] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[1] ^ state[2]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[1] = (byte) ((state[1] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[2] ^ state[3]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[2] = (byte) ((state[2] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[3] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[3] = (byte) ((state[3] ^ buf3 ^ buf1) & 0xff);\n            // col2\n            buf1 = (byte) ((state[4] ^ state[5] ^ state[6] ^ state[7]) & 0xff);\n            buf2 = state[4];\n            buf3 = (byte) ((state[4] ^ state[5]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[4] = (byte) ((state[4] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[5] ^ state[6]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[5] = (byte) ((state[5] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[6] ^ state[7]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[6] = (byte) ((state[6] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[7] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[7] = (byte) ((state[7] ^ buf3 ^ buf1) & 0xff);\n            // col3\n            buf1 = (byte) ((state[8] ^ state[9] ^ state[10] ^ state[11]) & 0xff);\n            buf2 = state[8];\n            buf3 = (byte) ((state[8] ^ state[9]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[8] = (byte) ((state[8] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[9] ^ state[10]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[9] = (byte) ((state[9] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[10] ^ state[11]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[10] = (byte) ((state[10] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[11] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[11] = (byte) ((state[11] ^ buf3 ^ buf1) & 0xff);\n            // col4\n            buf1 = (byte) ((state[12] ^ state[13] ^ state[14] ^ state[15]) & 0xff);\n            buf2 = state[12];\n            buf3 = (byte) ((state[12] ^ state[13]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[12] = (byte) ((state[12] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[13] ^ state[14]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[13] = (byte) ((state[13] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[14] ^ state[15]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[14] = (byte) ((state[14] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[15] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[15] = (byte) ((state[15] ^ buf3 ^ buf1) & 0xff);\n        }\n        // 10th round without mixcols\n        state[0] = sbox[(state[0] ^ expandedKey[(round * 16)]) & 0xff];\n        state[4] = sbox[(state[4] ^ expandedKey[(round * 16) + 4]) & 0xff];\n        state[8] = sbox[(state[8] ^ expandedKey[(round * 16) + 8]) & 0xff];\n        state[12] = sbox[(state[12] ^ expandedKey[(round * 16) + 12]) & 0xff];\n        // row 1\n        buf1 = (byte) ((state[1] ^ expandedKey[(round * 16) + 1]) & 0xff);\n        state[1] = sbox[(state[5] ^ expandedKey[(round * 16) + 5]) & 0xff];\n        state[5] = sbox[(state[9] ^ expandedKey[(round * 16) + 9]) & 0xff];\n        state[9] = sbox[(state[13] ^ expandedKey[(round * 16) + 13]) & 0xff];\n        state[13] = sbox[buf1 & 0xff];\n        // row 2\n        buf1 = (byte) ((state[2] ^ expandedKey[(round * 16) + 2]) & 0xff);\n        buf2 = (byte) ((state[6] ^ expandedKey[(round * 16) + 6]) & 0xff);\n        state[2] = sbox[(state[10] ^ expandedKey[(round * 16) + 10]) & 0xff];\n        state[6] = sbox[(state[14] ^ expandedKey[(round * 16) + 14]) & 0xff];\n        state[10] = sbox[buf1 & 0xff];\n        state[14] = sbox[buf2 & 0xff];\n        // row 3\n        buf1 = (byte) ((state[15] ^ expandedKey[(round * 16) + 15]) & 0xff);\n        state[15] = sbox[(state[11] ^ expandedKey[(round * 16) + 11]) & 0xff];\n        state[11] = sbox[(state[7] ^ expandedKey[(round * 16) + 7]) & 0xff];\n        state[7] = sbox[(state[3] ^ expandedKey[(round * 16) + 3]) & 0xff];\n        state[3] = sbox[buf1 & 0xff];\n        // last addroundkey\n        state[0] ^= expandedKey[160];\n        state[1] ^= expandedKey[161];\n        state[2] ^= expandedKey[162];\n        state[3] ^= expandedKey[163];\n        state[4] ^= expandedKey[164];\n        state[5] ^= expandedKey[165];\n        state[6] ^= expandedKey[166];\n        state[7] ^= expandedKey[167];\n        state[8] ^= expandedKey[168];\n        state[9] ^= expandedKey[169];\n        state[10] ^= expandedKey[170];\n        state[11] ^= expandedKey[171];\n        state[12] ^= expandedKey[172];\n        state[13] ^= expandedKey[173];\n        state[14] ^= expandedKey[174];\n        state[15] ^= expandedKey[175];\n        return state;\n    }\n\n    // straight foreward aes decryption implementation\n//   the order of substeps is the exact reverse of decryption\n//   inverse functions:\n//       - addRoundKey is its own inverse\n//       - rsbox is inverse of sbox\n//       - rightshift instead of leftshift\n//       - invMixColumns = barreto + mixColumns\n//   no further subfunctions to save cycles for function calls\n//   no structuring with \"for (....)\" to save cycles\n    private static byte[] aes_decr(byte[] input, byte[] expandedKey) {\n        byte buf1, buf2, buf3;\n        byte round;\n        round = 9;\n        byte[] state = new byte[input.length];\n        for (int i = 0; i < input.length; i++) {\n            state[i] = input[i];\n        }\n        // initial addroundkey\n        state[0] ^= expandedKey[160];\n        state[1] ^= expandedKey[161];\n        state[2] ^= expandedKey[162];\n        state[3] ^= expandedKey[163];\n        state[4] ^= expandedKey[164];\n        state[5] ^= expandedKey[165];\n        state[6] ^= expandedKey[166];\n        state[7] ^= expandedKey[167];\n        state[8] ^= expandedKey[168];\n        state[9] ^= expandedKey[169];\n        state[10] ^= expandedKey[170];\n        state[11] ^= expandedKey[171];\n        state[12] ^= expandedKey[172];\n        state[13] ^= expandedKey[173];\n        state[14] ^= expandedKey[174];\n        state[15] ^= expandedKey[175];\n\n        // 10th round without mixcols\n        state[0] = (byte) ((rsbox[state[0] & 0xff] ^ expandedKey[(round * 16)]) & 0xff);\n        state[4] = (byte) ((rsbox[state[4] & 0xff] ^ expandedKey[(round * 16) + 4]) & 0xff);\n        state[8] = (byte) ((rsbox[state[8] & 0xff] ^ expandedKey[(round * 16) + 8]) & 0xff);\n        state[12] = (byte) ((rsbox[state[12] & 0xff] ^ expandedKey[(round * 16) + 12]) & 0xff);\n        // row 1\n        buf1 = (byte) ((rsbox[state[13] & 0xff] ^ expandedKey[(round * 16) + 1]) & 0xff);\n        state[13] = (byte) ((rsbox[state[9] & 0xff] ^ expandedKey[(round * 16) + 13]) & 0xff);\n        state[9] = (byte) ((rsbox[state[5] & 0xff] ^ expandedKey[(round * 16) + 9]) & 0xff);\n        state[5] = (byte) ((rsbox[state[1] & 0xff] ^ expandedKey[(round * 16) + 5]) & 0xff);\n        state[1] = buf1;\n        // row 2\n        buf1 = (byte) ((rsbox[state[2] & 0xff] ^ expandedKey[(round * 16) + 10]) & 0xff);\n        buf2 = (byte) ((rsbox[state[6] & 0xff] ^ expandedKey[(round * 16) + 14]) & 0xff);\n        state[2] = (byte) ((rsbox[state[10] & 0xff] ^ expandedKey[(round * 16) + 2]) & 0xff);\n        state[6] = (byte) ((rsbox[state[14] & 0xff] ^ expandedKey[(round * 16) + 6]) & 0xff);\n        state[10] = buf1;\n        state[14] = buf2;\n        // row 3\n        buf1 = (byte) ((rsbox[state[3] & 0xff] ^ expandedKey[(round * 16) + 15]) & 0xff);\n        state[3] = (byte) ((rsbox[state[7] & 0xff] ^ expandedKey[(round * 16) + 3]) & 0xff);\n        state[7] = (byte) ((rsbox[state[11] & 0xff] ^ expandedKey[(round * 16) + 7]) & 0xff);\n        state[11] = (byte) ((rsbox[state[15] & 0xff] ^ expandedKey[(round * 16) + 11]) & 0xff);\n        state[15] = buf1;\n\n        for (round = 8; round >= 0; round--) {\n            // barreto\n            //col1\n            buf1 = galois_mul2(galois_mul2((byte) ((state[0] ^ state[2]) & 0xff)));\n            buf2 = galois_mul2(galois_mul2((byte) ((state[1] ^ state[3]) & 0xff)));\n            state[0] ^= buf1;\n            state[1] ^= buf2;\n            state[2] ^= buf1;\n            state[3] ^= buf2;\n            //col2\n            buf1 = galois_mul2(galois_mul2((byte) ((state[4] ^ state[6]) & 0xff)));\n            buf2 = galois_mul2(galois_mul2((byte) ((state[5] ^ state[7]) & 0xff)));\n            state[4] ^= buf1;\n            state[5] ^= buf2;\n            state[6] ^= buf1;\n            state[7] ^= buf2;\n            //col3\n            buf1 = galois_mul2(galois_mul2((byte) ((state[8] ^ state[10]) & 0xff)));\n            buf2 = galois_mul2(galois_mul2((byte) ((state[9] ^ state[11]) & 0xff)));\n            state[8] ^= buf1;\n            state[9] ^= buf2;\n            state[10] ^= buf1;\n            state[11] ^= buf2;\n            //col4\n            buf1 = galois_mul2(galois_mul2((byte) ((state[12] ^ state[14]) & 0xff)));\n            buf2 = galois_mul2(galois_mul2((byte) ((state[13] ^ state[15]) & 0xff)));\n            state[12] ^= buf1;\n            state[13] ^= buf2;\n            state[14] ^= buf1;\n            state[15] ^= buf2;\n            // mixcolums //////////\n            // col1\n            buf1 = (byte) ((state[0] ^ state[1] ^ state[2] ^ state[3]) & 0xff);\n            buf2 = state[0];\n            buf3 = (byte) ((state[0] ^ state[1]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[0] = (byte) ((state[0] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[1] ^ state[2]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[1] = (byte) ((state[1] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[2] ^ state[3]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[2] = (byte) ((state[2] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[3] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[3] = (byte) ((state[3] ^ buf3 ^ buf1) & 0xff);\n            // col2\n            buf1 = (byte) ((state[4] ^ state[5] ^ state[6] ^ state[7]) & 0xff);\n            buf2 = state[4];\n            buf3 = (byte) ((state[4] ^ state[5]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[4] = (byte) ((state[4] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[5] ^ state[6]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[5] = (byte) ((state[5] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[6] ^ state[7]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[6] = (byte) ((state[6] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[7] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[7] = (byte) ((state[7] ^ buf3 ^ buf1) & 0xff);\n            // col3\n            buf1 = (byte) ((state[8] ^ state[9] ^ state[10] ^ state[11]) & 0xff);\n            buf2 = state[8];\n            buf3 = (byte) ((state[8] ^ state[9]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[8] = (byte) ((state[8] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[9] ^ state[10]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[9] = (byte) ((state[9] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[10] ^ state[11]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[10] = (byte) ((state[10] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[11] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[11] = (byte) ((state[11] ^ buf3 ^ buf1) & 0xff);\n            // col4\n            buf1 = (byte) ((state[12] ^ state[13] ^ state[14] ^ state[15]) & 0xff);\n            buf2 = state[12];\n            buf3 = (byte) ((state[12] ^ state[13]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[12] = (byte) ((state[12] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[13] ^ state[14]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[13] = (byte) ((state[13] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[14] ^ state[15]) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[14] = (byte) ((state[14] ^ buf3 ^ buf1) & 0xff);\n            buf3 = (byte) ((state[15] ^ buf2) & 0xff);\n            buf3 = galois_mul2(buf3);\n            state[15] = (byte) ((state[15] ^ buf3 ^ buf1) & 0xff);\n            // addroundkey, rsbox and shiftrows\n            // row 0\n            state[0] = (byte) ((rsbox[state[0] & 0xff] ^ expandedKey[(round * 16)]) & 0xff);\n            state[4] = (byte) ((rsbox[state[4] & 0xff] ^ expandedKey[(round * 16) + 4]) & 0xff);\n            state[8] = (byte) ((rsbox[state[8] & 0xff] ^ expandedKey[(round * 16) + 8]) & 0xff);\n            state[12] = (byte) ((rsbox[state[12] & 0xff] ^ expandedKey[(round * 16) + 12]) & 0xff);\n            // row 1\n            buf1 = (byte) ((rsbox[state[13] & 0xff] ^ expandedKey[(round * 16) + 1]) & 0xff);\n            state[13] = (byte) ((rsbox[state[9] & 0xff] ^ expandedKey[(round * 16) + 13]) & 0xff);\n            state[9] = (byte) ((rsbox[state[5] & 0xff] ^ expandedKey[(round * 16) + 9]) & 0xff);\n            state[5] = (byte) ((rsbox[state[1] & 0xff] ^ expandedKey[(round * 16) + 5]) & 0xff);\n            state[1] = buf1;\n            // row 2\n            buf1 = (byte) ((rsbox[state[2] & 0xff] ^ expandedKey[(round * 16) + 10]) & 0xff);\n            buf2 = (byte) ((rsbox[state[6] & 0xff] ^ expandedKey[(round * 16) + 14]) & 0xff);\n            state[2] = (byte) ((rsbox[state[10] & 0xff] ^ expandedKey[(round * 16) + 2]) & 0xff);\n            state[6] = (byte) ((rsbox[state[14] & 0xff] ^ expandedKey[(round * 16) + 6]) & 0xff);\n            state[10] = buf1;\n            state[14] = buf2;\n            // row 3\n            buf1 = (byte) ((rsbox[state[3] & 0xff] ^ expandedKey[(round * 16) + 15]) & 0xff);\n            state[3] = (byte) ((rsbox[state[7] & 0xff] ^ expandedKey[(round * 16) + 3]) & 0xff);\n            state[7] = (byte) ((rsbox[state[11] & 0xff] ^ expandedKey[(round * 16) + 7]) & 0xff);\n            state[11] = (byte) ((rsbox[state[15] & 0xff] ^ expandedKey[(round * 16) + 11]) & 0xff);\n            state[15] = buf1;\n        }\n        return state;\n    }\n\n    public static byte[] decode16(byte[] data, byte[] key) {\n        byte[] expandedKey = expandKey(key);\n        return aes_decr(data, expandedKey);\n    }\n\n    public static byte[] encode16(byte[] data, byte[] key) {\n        byte[] expandedKey = expandKey(key);\n        return aes_encr(data, expandedKey);\n    }\n\n    public static byte[] decode(byte[] data, byte[] key) {\n        byte[] result = new byte[0];\n        for (int i = 0; i < data.length / 16; i++) {\n            byte[] temp = ByteUtils.subBytes(data, i * 16, 16);\n            result = ByteUtils.combineByteArray(result, decode16(temp, key));\n        }\n        return result;\n    }\n\n    public static byte[] encode(byte[] data, byte[] key) {\n        byte[] result = new byte[0];\n        for (int i = 0; i < data.length / 16; i++) {\n            byte[] temp = ByteUtils.subBytes(data, i * 16, 16);\n            result = ByteUtils.combineByteArray(result, encode16(temp, key));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/encryption/CRC.java",
    "content": "package com.luo.bluetooth.encryption;\n\npublic class CRC {\n    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,\n            (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,\n            (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,\n            (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 };\n\n    /**\n     * 计算数组的CRC8校验值\n     *\n     * @param data\n     *            需要计算的数组\n     * @return CRC8校验值\n     */\n    public static byte calcCrc(byte[] data) {\n        return calcCrc8(data, 0, data.length, (byte) 0);\n    }\n\n    /**\n     * 计算CRC8校验值\n     *\n     * @param data\n     *            数据\n     * @param offset\n     *            起始位置\n     * @param len\n     *            长度\n     * @return 校验值\n     */\n    public static byte calcCrc8(byte[] data, int offset, int len) {\n        byte result = data[0];\n        for (int i = 1; i < len; i++) {\n            result ^= data[i];\n        }\n        return result;\n//        return calcCrc8(data, offset, len, (byte) 0);\n    }\n\n    /**\n     * 计算CRC8校验值\n     *\n     * @param data\n     *            数据\n     * @param offset\n     *            起始位置\n     * @param len\n     *            长度\n     * @param preval\n     *            之前的校验值\n     * @return 校验值\n     */\n    public static byte calcCrc8(byte[] data, int offset, int len, byte preval) {\n        byte ret = preval;\n        for (int i = offset; i < (offset + len); ++i) {\n            ret = crc8_tab[(0x00ff & (ret ^ data[i]))];\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/CRC16Utils.java",
    "content": "/**\n * Copyright (C), 2007-2021, 未来穿戴有限公司\n * FileName: CRC16Utils\n * Author: Antier\n * Date: 2021/7/16 11:46\n * Description: 用一句话描述下\n */\npackage com.luo.bluetooth.protocol;\n\nimport android.util.Log;\n\n/**\n * @ProjectName: SKG\n * @Package: com.king.bluetooth.protocol.neck.util\n * @ClassName: CRC16Utils\n * @Description: 用一句话描述下\n * @Author: Aniter\n * @CreateDate: 2021/7/16 11:46\n */\nclass CRC16Utils {\n    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,\n            (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,\n            (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,\n            (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,\n            (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};\n\n    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,\n            (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,\n            (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,\n            (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,\n            (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};\n\n    /**\n     * 计算CRC16校验  对外的接口\n     *\n     * @param data 需要计算的数组\n     * @return CRC16校验值\n     */\n    public static int calcCrc16(byte[] data) {\n        return calcCrc16(data, 0, data.length);\n    }\n\n    /**\n     * 计算CRC16校验\n     *\n     * @param data   需要计算的数组\n     * @param offset 起始位置\n     * @param len    长度\n     * @return CRC16校验值\n     */\n    public static int calcCrc16(byte[] data, int offset, int len) {\n        return calcCrc16(data, offset, len, 0xffff);\n    }\n\n    /**\n     * 计算CRC16校验\n     *\n     * @param data   需要计算的数组\n     * @param offset 起始位置\n     * @param len    长度\n     * @param preval 之前的校验值\n     * @return CRC16校验值\n     */\n    public static int calcCrc16(byte[] data, int offset, int len, int preval) {\n        int ucCRCHi = (preval & 0xff00) >> 8;\n        int ucCRCLo = preval & 0x00ff;\n        int iIndex;\n        for (int i = 0; i < len; ++i) {\n            iIndex = (ucCRCLo ^ data[offset + i]) & 0x00ff;\n            ucCRCLo = ucCRCHi ^ crc16_tab_h[iIndex];\n            ucCRCHi = crc16_tab_l[iIndex];\n        }\n        return ((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff;\n    }\n\n    /**\n     * 将计算的CRC值 转换为加空格的  比如  ： crc值为 A30A -> A3 0A\n     *\n     * @param res\n     * @return\n     */\n    public static String getCrc(int res) {\n        String format = String.format(\"%04x\", res);\n        String substring = format.substring(0, 2);\n        String substring1 = format.substring(2, 4);\n        Log.i(\"BLUEDATA\", \"crc ---- : \" + substring + \"  \" + substring1);\n        return substring.concat(\" \").concat(substring1).concat(\" \");\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/CrcUtils.java",
    "content": "/**\n * Copyright (C), 2007-2022, 未来穿戴有限公司\n * FileName: CrcUtils\n * Author: lpq\n * Date: 2022/3/11 17:23\n * Description: 用一句话描述下\n */\npackage com.luo.bluetooth.protocol;\n\n/**\n *\n * @ProjectName: Bluetooth\n * @Package: com.luo.bluetooth.protocol\n * @ClassName: CrcUtils\n * @Description: 用一句话描述下\n * @Author: lpq\n * @CreateDate: 2022/3/11 17:23\n */\npublic class CrcUtils {\n    /**\n     * crc校验\n     */\n    public static int Crc16Ccitt(int crc, byte[] data) {\n        for (int i = 0; i < data.length; ++i) {\n            crc = (crc >> 8) ^ s_tabCrc16CCITT[(crc ^ (data[i])) & 0xff];\n        }\n        return crc;\n    }\n\n    /*\n     * CRC table for the CRC-16-CCITT. The poly is 0x8408 (x^16 + x^12 + x^5 + 1)\n     */\n    static int[] s_tabCrc16CCITT = new int[]{\n            0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,\n            0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,\n            0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,\n            0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,\n            0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,\n            0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,\n            0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,\n            0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,\n            0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,\n            0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,\n            0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,\n            0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,\n            0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,\n            0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,\n            0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,\n            0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,\n            0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,\n            0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,\n            0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,\n            0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,\n            0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,\n            0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,\n            0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,\n            0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,\n            0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,\n            0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,\n            0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,\n            0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,\n            0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,\n            0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,\n            0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,\n            0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78\n    };\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/CustomEventListener.java",
    "content": "package com.luo.bluetooth.protocol;\n\nimport com.stag.bluetooth.protocol.OnEventListener;\n\n/**\n * 一些由设备端主动发起的蓝牙事件的监听\n */\npublic interface CustomEventListener extends OnEventListener {\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/CustomPacket.java",
    "content": "package com.luo.bluetooth.protocol;\n\nimport com.stag.bluetooth.packet.Packet;\n\npublic class CustomPacket extends Packet {\n    private byte expandCode; // 扩展码：0x55->读；0x66->写；0xAA->读回应；0x99->写回应\n\n    public CustomPacket(int cmd, byte expandCode) {\n        super(cmd);\n        this.expandCode = expandCode;\n    }\n\n    public CustomPacket(int cmd, byte[] data, byte expandCode) {\n        super(cmd, data);\n        this.expandCode = expandCode;\n    }\n\n    public byte getExpandCode() {\n        return expandCode;\n    }\n\n    /**\n     * 用于判断发送的命令是否已经收到回应，没有收到回应底层会重发一次数据\n     * @param recvPacket 接收到的字节数据处理的结果包\n     * @return\n     */\n    @Override\n    public boolean match(Packet recvPacket) {\n        return super.match(recvPacket);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/CustomProtocol.java",
    "content": "package com.luo.bluetooth.protocol;\n\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.luo.bluetooth.utils.ByteUtils;\nimport com.stag.bluetooth.protocol.ParseResult;\nimport com.stag.bluetooth.protocol.Protocol;\nimport com.stag.bluetooth.protocol.ResultType;\nimport com.stag.bluetooth.util.LogUtils;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class CustomProtocol extends Protocol<CustomPacket, CustomEventListener> {\n\n    private static final String TAG = \"DscpProtocol\";\n\n    private final Object recvParseLock = null;\n\n    private final byte FRAME_HEADER = 0x7B;  // 帧头\n    private final byte DST_PATH_LEN = 0x01;  // 目标路径长度: 多设备拓展长设置，目前只有0x01\n    private final byte DEVICE_ADDRESS = 0x10;  // 地址区域：01->血糖仪；0x20->主机设备\n    private final byte PHONE_ADDRESS = 0x20;   // 地址区域：01->血糖仪；0x20->主机设备\n    private final byte SRC_PATH_LEN = 0x01;  // 源路径长度：多设备拓展长设置，目前只有0x01\n    private final byte EXPAND_CODE = 0x55;   // 扩展码：0x55->读；0x66->写；0xAA->读回应；0x99->写回应\n    private final byte FRAME_END = 0x7D;     // 帧尾\n    private final int BUFFER_SIZE = 256;\n\n    private ByteBuffer recvBuffer;\n    private CustomPacket recvPacket;\n\n    protected CustomProtocol(Context context) {\n        super(context);\n    }\n\n    public CustomProtocol(Context context, CustomEventListener listener) {\n        super(context, listener);\n    }\n\n    @Override\n    public synchronized byte[] packetToBytes(CustomPacket packet) {\n        /**\n         * 0x7B：帧头\n         * 0x01：目标路径长度\n         * 0x10：目标地址区域\n         * 0x01：源路径长度\n         * 0x20：源地址区域\n         * 0x77：获取SN信息功能码\n         * 0x55：扩展码 read\n         * 0x00 0x00：通讯数据长度\n         * 0x00 ...0x00：通讯数据\n         * 0x01 0x0B 0x0B 0x04：校验码\n         * 0x7D：帧尾\n         */\n        //实际打包\n        int cmd = packet.getCmd();\n        byte[] cmdData = packet.getData();\n        byte expandCode = packet.getExpandCode();\n        ArrayList<Byte> byteList = new ArrayList<Byte>();\n        // 1、加入目标路径长度，目标地址区域，源路径长度，源地址区域\n        byteList.add(DST_PATH_LEN);\n        byteList.add(DEVICE_ADDRESS);\n        byteList.add(SRC_PATH_LEN);\n        byteList.add(PHONE_ADDRESS);\n        LogUtils.d(TAG + \"lpq\", \"1、加入基础头部：\" + ByteUtils.toString(listToByte(byteList)));\n        // 2、加入功能码\n        byteList.add((byte) cmd);\n        LogUtils.d(TAG + \"lpq\", \"2、加入功能码：\" + ByteUtils.toString(listToByte(byteList)));\n        // 3、加入扩展码\n        byteList.add(expandCode);\n        LogUtils.d(TAG + \"lpq\", \"3、加入扩展码：\" + ByteUtils.toString(listToByte(byteList)));\n        // 4、加入通讯数据\n        if (cmdData != null && cmdData.length != 0) {\n            byte[] cmdDataLenBytes = ByteUtils.intToBytes2(cmdData.length);\n            byteList.add(cmdDataLenBytes[0]);\n            byteList.add(cmdDataLenBytes[1]);\n            for (byte temp : cmdData) {\n                byteList.add(temp);\n            }\n        } else {\n            byteList.add((byte) 0x0);\n            byteList.add((byte) 0x0);\n        }\n        Log.i(\"lpq\", \"packetToBytes: 4、加入通讯数据：\" + ByteUtils.toString(listToByte(byteList)));\n        // 5、加入校验码\n        int crcResult = CRC16Utils.calcCrc16(listToByte(byteList));\n        byte[] crcBytes = ByteUtils.intToBytes2(crcResult);\n        Log.i(\"lpq\", \"packetToBytes: 5、校验码原值：\" + ByteUtils.toString(crcBytes));\n        byteList.add((byte) ((crcBytes[1] >> 4) & 0x0f));\n        byteList.add((byte) (crcBytes[1] & 0x0f));\n        byteList.add((byte) ((crcBytes[0] >> 4) & 0x0f));\n        byteList.add((byte) (crcBytes[0] & 0x0f));\n        LogUtils.d(TAG + \"lpq\", \"5、加入校验码后：\" + ByteUtils.toString(listToByte(byteList)));\n\n        // 6、加入头尾帧\n        byteList.add(0, FRAME_HEADER);\n        byteList.add(FRAME_END);\n        LogUtils.d(TAG + \"lpq\", \"6、加入头尾帧：\" + ByteUtils.toString(listToByte(byteList)));\n\n        return listToByte(byteList);\n    }\n\n    @Override\n    public ParseResult parse(byte[] data) {\n        LogUtils.d(TAG + \"lpq\", \"蓝牙接收到数据：data = \" + ByteUtils.toString(data));\n        recvPacket = null;\n        final ParseResult result = new ParseResult();\n        if (recvBuffer.position() > 0) {\n            if (recvBuffer.get(0) != FRAME_HEADER) {\n                recvBuffer.clear();\n            }\n        }\n        recvBuffer.put(data);\n        LogUtils.d(TAG + \"lpq\", \"parse: \" + ByteUtils.toString(recvBuffer.array()));\n        LogUtils.d(TAG + \"lpq\", \"parse: position = \" + recvBuffer.position());\n        if (recvBuffer.get(recvBuffer.position() - 1) != FRAME_END) {\n            result.setType(ResultType.INCOMPLETE);\n            return result;\n        }\n        synchronized (recvParseLock) {\n            byte[] temp = ByteUtils.subBytes(recvBuffer.array(), 0, recvBuffer.position());\n            List<Byte> byteList = byteToList(temp);\n\n            //开始解包\n            // 1、去掉包头包尾\n            byteList.remove(0);\n            byteList.remove(byteList.size() - 1);\n            LogUtils.d(TAG + \"lpq\", \"1、去掉包头包尾后：\" + ByteUtils.toString(listToByte(byteList)));\n\n            // 2、CRC校验\n            // 获取校验码\n            byte[] crcOrigin = new byte[4];\n            crcOrigin[3] = byteList.remove(byteList.size() - 1);\n            crcOrigin[2] = byteList.remove(byteList.size() - 1);\n            crcOrigin[1] = byteList.remove(byteList.size() - 1);\n            crcOrigin[0] = byteList.remove(byteList.size() - 1);\n            // 计算校验码\n            int crcResult = CRC16Utils.calcCrc16(listToByte(byteList));\n            byte[] crcBytes = ByteUtils.intToBytes2(crcResult);\n            Log.i(\"lpq\", \"packetToBytes: 2、校验码原值：\" + ByteUtils.toString(crcOrigin));\n            byte[] crcCount = new byte[4];\n            crcCount[0] = ((byte) ((crcBytes[1] >> 4) & 0x0f));\n            crcCount[1] = ((byte) (crcBytes[1] & 0x0f));\n            crcCount[2] = ((byte) ((crcBytes[0] >> 4) & 0x0f));\n            crcCount[3] = ((byte) (crcBytes[0] & 0x0f));\n            Log.i(\"lpq\", \"packetToBytes: 2、校验码计算值：\" + ByteUtils.toString(crcCount));\n            if (crcOrigin[0] == crcCount[0] && crcOrigin[1] == crcCount[1]\n                    && crcOrigin[2] == crcCount[2] && crcOrigin[3] == crcCount[3]) {\n                Log.i(\"lpq\", \"parse: 校验成功\");\n            } else {\n                Log.i(\"lpq\", \"parse: 校验失败\");\n            }\n\n            // 3、去除目标路径长度，目标地址区域，源路径长度，源地址区域\n            byteList.remove(0);\n            byteList.remove(0);\n            byteList.remove(0);\n            byteList.remove(0);\n            LogUtils.d(TAG + \"lpq\", \"3、去除基础头部：\" + ByteUtils.toString(listToByte(byteList)));\n\n            // 4、获取功能码\n            int cmd = byteList.remove(0);\n            byte expandCode = byteList.remove(0);\n            LogUtils.d(TAG + \"lpq\", \"4、获取功能码：\" + ByteUtils.toString(listToByte(byteList)));\n\n            // 5、获取通讯数据\n            byte[] lenBytes = new byte[2];\n            lenBytes[0] = byteList.remove(0);\n            lenBytes[1] = byteList.remove(0);\n            int cmdDataLen = ByteUtils.bytesToInt(lenBytes);\n            byte[] cmdData = listToByte(byteList);\n            LogUtils.d(TAG + \"lpq\", \"5、获取通讯数据：\" + ByteUtils.toString(listToByte(byteList)));\n\n            recvPacket = new CustomPacket(cmd, cmdData, expandCode);\n            LogUtils.d(TAG + \"lpq\", \"parse: \" + recvPacket.toString());\n        }\n        result.setPacket(recvPacket);\n        switch (recvPacket.getCmd()) {\n            // 设备主动上传设备状态\n//                if (haveSetEventListener()) {\n//                    result.setCallback(new BluetoothDispatch.Callback() {\n//                        @Override\n//                        public void callback() {\n//                            getEventListener().onRecvDeviceStatus(recvPacket.getData());\n//                        }\n//                    });\n//                }\n//                result.setType(ACTIVE);\n            default://理论上都是待响应事件\n                result.setType(ResultType.RESPOND);\n                break;\n        }\n\n        recvBuffer.clear();\n        return result;\n    }\n\n    /**\n     * list转换成byte\n     */\n    private byte[] listToByte(List<Byte> byteList) {\n        if (null == byteList || byteList.size() == 0) {\n            return null;\n        }\n        byte[] bytes = new byte[byteList.size()];\n        for (int i = 0; i < byteList.size(); ++i) {\n            bytes[i] = byteList.get(i);\n        }\n        return bytes;\n    }\n\n    private List<Byte> byteToList(byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        }\n        List<Byte> byteList = new ArrayList<Byte>();\n        for (int i = 0; i < bytes.length; ++i) {\n            byteList.add(bytes[i]);\n        }\n        return byteList;\n    }\n\n    private void addBytes(ArrayList<Byte> byteList, byte[] encodeBytes) {\n        for (int i = 0; i < encodeBytes.length; i++) {\n            byteList.add(encodeBytes[i]);\n        }\n    }\n\n    /**\n     * 协议类型，用于支持多协议时的区别\n     *\n     * @return\n     */\n    @Override\n    public int getType() {\n        return 0;\n    }\n\n    @Override\n    public UUID getServiceUUID() {\n        return UUID.fromString(\"0003CDD0-0000-1000-8000-00805F9B0131\");\n    }\n\n    @Override\n    public UUID getSendTunnelUUID() {\n        return UUID.fromString(\"0003CDD2-0000-1000-8000-00805F9B0131\");\n    }\n\n    @Override\n    public UUID getRecvTunnelUUID() {\n        return UUID.fromString(\"0003CDD1-0000-1000-8000-00805F9B0131\");\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/OnTimeoutResult.java",
    "content": "/*\n * Copyright (c) 2019. stag All rights reserved.\n */\n\npackage com.luo.bluetooth.protocol;\n\n/**\n * 蓝牙任务回调\n */\npublic interface OnTimeoutResult<T> {\n\n    void onResult(boolean isTimeout, T result);\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/WeiCeCode.java",
    "content": "/**\n * Copyright (C), 2007-2022, 未来穿戴有限公司\n * FileName: WeiCeCode\n * Author: lpq\n * Date: 2022/3/11 16:27\n * Description: 用一句话描述下\n */\npackage com.luo.bluetooth.protocol;\n\n/**\n *\n * @ProjectName: Bluetooth\n * @Package: com.luo.bluetooth.protocol\n * @ClassName: WeiCeCode\n * @Description: 用一句话描述下\n * @Author: lpq\n * @CreateDate: 2022/3/11 16:27\n */\npublic class WeiCeCode {\n    /**\n     * ExpandCode\n     */\n    public static final byte EXPAND_CODE_READ = 0x55;\n    public static final byte EXPAND_CODE_WRITE = 0x66;\n    public static final byte EXPAND_CODE_READ_RESPONSE = (byte) 0xAA;\n    public static final byte EXPAND_CODE_WRITE_RESPONSE = (byte) 0x99;\n\n    /**\n     * 获取SN\n     */\n    public static final int GET_SN = 0x77;\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/protocol/WeiCeDeviceOperate.java",
    "content": "\n\n/*\n * Copyright (c) 2019. stag All rights reserved.\n */\n\npackage com.luo.bluetooth.protocol;\n\nimport android.util.Log;\n\nimport com.stag.bluetooth.BluetoothTask;\nimport com.stag.bluetooth.util.ByteUtils;\n\nimport java.nio.ByteBuffer;\n\npublic class WeiCeDeviceOperate {\n    private static final String TAG = \"DncpDeviceOperate\";\n    public static int ERASE_TIMEOUT = 3000;\n    public static int ERASE_TRY_COUNT = 2;\n\n    public static void getSn(final OnTimeoutResult<String> obtainResult, boolean callbackInMainThread) {\n        BluetoothTask task = new BluetoothTask(new CustomPacket(WeiCeCode.GET_SN, WeiCeCode.EXPAND_CODE_READ),\n                callbackInMainThread,\n                new BluetoothTask.OnDataResultListener() {\n                    @Override\n                    public void onResult(boolean isTimeout, byte[] data) {\n                        Log.i(\"lpq\", \"onResult: data = \" + ByteUtils.toString(data));\n                        String sn = null;\n                        if (!isTimeout && data != null) {\n                            ByteBuffer byteBuffer = ByteBuffer.wrap(data);\n                            sn = new String(byteBuffer.array()).replaceAll(\"[^\\u4E00-\\u9FA5a-zA-Z0-9-_]+\", \"\");\n                        }\n                        Log.i(\"lpq\", \"onResult: sn = \" + sn);\n                        obtainResult.onResult(isTimeout, sn);\n                    }\n                });\n        task.setTimeout(ERASE_TIMEOUT);\n        task.send();\n    }\n\n\n//    public String getTypeBySync() {\n//        byte[] data = new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_TYPE, address)).sendBySync2();\n//        String type = null;\n//        if (data != null) {\n//            ByteBuffer byteBuffer = ByteBuffer.wrap(data);\n//            type = new String(byteBuffer.array()).replaceAll(\"[^\\u4E00-\\u9FA5a-zA-Z0-9-_]+\", \"\");\n//        }\n//        return type;\n//    }\n\n\n//    public void setType(String type, final OnTimeoutResult<Boolean> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_TYPE,\n//                ByteUtils.wrapData(type.getBytes(), 16),\n//                address),\n//                callbackInMainThread,\n//                obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Boolean res = Boolean.FALSE;\n//                        if (!isTimeout && data != null) {\n//                            res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getModelNumber(@NonNull final OnTimeoutResult<String> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_MODEL, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        String modelNumber = null;\n//                        if (!isTimeout && data != null) {\n//                            ByteBuffer byteBuffer = ByteBuffer.wrap(data);\n//                            modelNumber = new String(byteBuffer.array()).replaceAll(\"[^\\u4E00-\\u9FA5a-zA-Z0-9-_]+\", \"\");\n//                        }\n//                        obtainResult.onResult(isTimeout, modelNumber);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void setModelNumber(String model, final OnTimeoutResult<Boolean> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_MODEL,\n//                ByteUtils.wrapData(model.getBytes(), 16),\n//                address),\n//                callbackInMainThread,\n//                obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Boolean res = Boolean.FALSE;\n//                        if (!isTimeout && data != null) {\n//                            res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getManufacturer(@NonNull final OnTimeoutResult<String> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_MANUFACTURER, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        String manufacturer = null;\n//                        if (!isTimeout && data != null) {\n//                            ByteBuffer byteBuffer = ByteBuffer.wrap(data);\n//                            manufacturer = new String(byteBuffer.array()).replace(\"\\0\", \"\");\n//                            //manufacturer = new String(byteBuffer.array()).replaceAll(\"[^\\u4E00-\\u9FA5a-zA-Z0-9-_]+\", \"\");\n//                        }\n//                        obtainResult.onResult(isTimeout, manufacturer);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void setManufacturer(String manufacturer, final OnTimeoutResult<Boolean> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_SET_MANUFACTURER,\n//                ByteUtils.wrapData(manufacturer.getBytes(), 20),\n//                address),\n//                callbackInMainThread,\n//                obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Boolean res = Boolean.FALSE;\n//                        if (!isTimeout && data != null) {\n//                            res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getHardWareVersion(@NonNull final OnTimeoutResult<Version> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_HARDWARE_VERSION, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n////                        Log.e(\"软件/硬件版本号\", \"硬件版本号：\" + (data == null ? \"是个空\" : StringUtils.byteArrayToHexString(data)));\n//\n//                        Version version = null;\n//                        if (!isTimeout && data != null) {\n//                            version = Version.getVersion(data);\n//                        }\n//                        obtainResult.onResult(isTimeout, version);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getSoftWareVersion(@NonNull final OnTimeoutResult<Version> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SOFT_VERSION, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Version version = null;\n//                        if (!isTimeout && data != null) {\n//                            version = Version.getVersion(data);\n//                        }\n//                        obtainResult.onResult(isTimeout, version);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public Version getSoftWareVersionBySync() {\n//        BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SOFT_VERSION, address));\n//        task.setTimeout(1500);\n//        task.setTryCount(2);\n//        byte[] data = task.sendBySync2();\n//        Version version = null;\n//        if (data != null) {\n//            version = Version.getVersion(data);\n//        }\n//        return version;\n//    }\n//\n//\n//    public void getSerialNumber(@NonNull final OnTimeoutResult<String> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceInfoCode.DSCP_CMD_DII_GET_SN, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        String serialNumber = null;\n//                        if (!isTimeout && data != null) {\n//                            ByteBuffer byteBuffer = ByteBuffer.wrap(data);\n//                            serialNumber = new String(byteBuffer.array()).replaceAll(\"[^\\u4E00-\\u9FA5a-zA-Z0-9-_]+\", \"\");\n//                        }\n//                        obtainResult.onResult(isTimeout, serialNumber);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getRunMode(@NonNull final OnTimeoutResult<DeviceRunMode> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_RUN_MODE, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        DeviceRunMode mode = null;\n//                        if (!isTimeout && data != null) {\n//                            mode = DeviceRunMode.getDeviceRunMode(data[0]);\n//                        }\n//                        obtainResult.onResult(isTimeout, mode);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public DeviceRunMode getRunModeBySync() {\n//        byte[] data = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_RUN_MODE, address))\n//                .setTimeout(500)\n//                .sendBySync2();\n//        DeviceRunMode mode = null;\n//        if (data != null) {\n//            mode = DeviceRunMode.getDeviceRunMode(data[0]);\n//        }\n//        return mode;\n//    }\n//\n//\n//    public void getVersion(@NonNull final OnTimeoutResult<Version> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_VERSION, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Version version = null;\n//                        if (!isTimeout && data != null) {\n//                            version = Version.getVersion(data);\n//                        }\n//                        obtainResult.onResult(isTimeout, version);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public void getMaxFragmentSize(@NonNull final OnTimeoutResult<Short> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_MAX_FRAGMENT_SIZE, address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Short res = null;\n//                        if (!isTimeout && data != null) {\n//                            res = Short.valueOf(ByteUtils.bytesToShort(data));\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).send();\n//    }\n//\n//\n//    public short getMaxFragmentSizeBySync() {\n//        byte[] data = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_GET_MAX_FRAGMENT_SIZE, address)).sendBySync2();\n//        short res = 0;\n//        if (data != null) {\n//            res = ByteUtils.bytesToShort(data);\n//        }\n//        return res;\n//    }\n//\n//\n//    public void enterUpdater() {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ENTER_UPDATER, address)).send();\n//    }\n//\n//\n//    public void enterApp() {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ENTER_APPLICATION, address)).send();\n//    }\n//\n//\n//    public void erase(int address, int size, final OnTimeoutResult<Boolean> obtainResult, boolean callbackInMainThread) {\n//        byte[] data = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address), ByteUtils.intToBytes(size)), 8);\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_ERASE, data, this.address),\n//                callbackInMainThread,\n//                obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Boolean res = Boolean.FALSE;\n//                        if (!isTimeout && data != null) {\n//                            res = ByteUtils.bytesToShort(data) == DSCPCode.DSCP_OK;\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).setTimeout(ERASE_TIMEOUT).setTryCount(ERASE_TRY_COUNT).send();\n//    }\n//\n//\n//    public void writeProgram(int address, int seq, int length, byte[] data,\n//                             final OnTimeoutResult<WriteProgramResult> obtainResult, boolean callbackInMainThread) {\n//        byte[] temp = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address),\n//                ByteUtils.shortToBytes((short) (length & 0xffff)), ByteUtils.shortToBytes((short) (seq & 0xffff)), data), length + 8);\n//        BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_WRITE_PROGRAM, temp, this.address),\n//                callbackInMainThread,\n//                obtainResult == null ? null : new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        WriteProgramResult res = null;\n//                        if (!isTimeout && data != null) {\n//                            res = WriteProgramResult.getWriteProgramResult(data);\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                });\n//        task.setTimeout(2000);\n//        task.setTryCount(1);\n//        task.send();\n//    }\n//\n//    public WriteProgramResult writeProgramBySync(int address, int seq, int length, byte[] data) {\n//        byte[] temp = ByteUtils.wrapData(ByteUtils.combineByteArray(ByteUtils.intToBytes(address),\n//                ByteUtils.shortToBytes((short) (length & 0xffff)), ByteUtils.shortToBytes((short) (seq & 0xffff)), data), length + 8);\n//        BluetoothTask task = new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_WRITE_PROGRAM, temp, this.address));\n//        task.setTimeout(2000);\n//        task.setTryCount(1);\n//        byte[] d = task.sendBySync2();\n//        WriteProgramResult res = null;\n//        if (d != null) {\n//            res = WriteProgramResult.getWriteProgramResult(d);\n//        }\n//        return res;\n//    }\n//\n//\n//    public void checkIntegrity(int checksum, @NonNull final OnTimeoutResult<Boolean> obtainResult, boolean callbackInMainThread) {\n//        new BluetoothTask(new DncpPacket(DeviceUpdateCode.DSCP_CMD_DUI_CHECK_INTEGRITY, ByteUtils.intToBytes2SmallDian(checksum), address),\n//                callbackInMainThread,\n//                new BluetoothTask.OnDataResultListener() {\n//                    @Override\n//                    public void onResult(boolean isTimeout, byte[] data) {\n//                        Boolean res = Boolean.FALSE;\n//                        if (!isTimeout && data != null) {\n//                            res = ByteUtils.bytes2ToInt2(data) == DSCPCode.DSCP_OK;\n//                        }\n//                        obtainResult.onResult(isTimeout, res);\n//                    }\n//                }).send();\n//    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/ByteUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.UUID;\n\nimport lombok.NonNull;\n\n/**\n * Created by Administrator on 2016/7/25 0025.\n */\npublic class ByteUtils {\n    final protected static char[] hexArray = \"0123456789ABCDEF\".toCharArray();\n\n    public static String bytesToHex(byte[] bytes) {\n        StringBuilder r = new StringBuilder(bytes.length * 2);\n        for (byte b : bytes) {\n            r.append(hexArray[(b >> 4) & 0xF]);\n            r.append(hexArray[(b & 0xF)]);\n        }\n        return r.toString();\n\n    }\n\n    /**\n     * 缺失部分有效数字\n     * UUID型转成4字节byte 大端模式，UUID如果不足处于后补零，有效数字在前\n     *\n     * @param u\n     * @return\n     */\n    public static byte[] uuidToByte5BigEndian2(UUID u) {\n        long l = u.getMostSignificantBits();\n        byte[] b = new byte[5];\n        b[4] = (byte) ((l >> 24) & 0xf000000fL);\n        b[3] = (byte) ((l >> 32) & 0xff00000000L);\n        b[2] = (byte) ((l >> 40) & 0xff0000000000L);\n        b[1] = (byte) ((l >> 48) & 0xff000000000000L);\n        b[0] = (byte) ((l >> 56) & 0xff00000000000000L);\n        return b;\n    }\n\n    /**\n     * 4字节byte转化成UUID 大端模式，UUID如果不足处于后补零，有效数字在前\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte5ToUuidBigEndian2(byte[] b) {\n        long l = ((long) b[4] << 24) & 0xff000000L;\n        l |= ((long) b[3] << 32) & 0xff00000000L;\n        l |= ((long) b[2] << 40) & 0xff0000000000L;\n        l |= ((long) b[1] << 48) & 0xff000000000000L;\n        l |= ((long) b[0] << 56) & 0xff00000000000000L;\n        return new UUID(l, 0);\n    }\n\n    /**\n     * 缺失部分有效数字\n     * UUID型转成4字节byte 大端模式\n     *\n     * @param u\n     * @return\n     */\n    public static byte[] uuidToByte5BigEndian(UUID u) {\n        long l = u.getLeastSignificantBits();\n        byte[] b = new byte[5];\n        b[4] = (byte) (l & 0xffL);\n        b[3] = (byte) ((l >> 8) & 0xff00L);\n        b[2] = (byte) ((l >> 16) & 0xff0000L);\n        b[1] = (byte) ((l >> 24) & 0xff000000L);\n        b[0] = (byte) ((l >> 32) & 0xff00000000L);\n        return b;\n    }\n\n    /**\n     * 4字节byte转化成UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte5ToUuidBigEndian(byte[] b) {\n        long l = b[4] & 0xffL;\n        l |= (b[3] << 8) & 0xff00L;\n        l |= (b[2] << 16) & 0xff0000L;\n        l |= (b[1] << 24) & 0xff000000L;\n        l |= (b[0] << 32) & 0xff00000000L;\n        return new UUID(0, l);\n    }\n\n    /**\n     * 大端模式将3个字节的Byte转成int型\n     *\n     * @param b\n     * @return\n     */\n    public static long byte3ToLongBigEndian(byte[] b) {\n        long i = (long) b[2] & 0xffL;\n        i |= (((long) b[1] << 8) & 0xff00L);\n        i |= (((long) b[1] << 16) & 0xff0000L);\n        return i;\n    }\n\n    /**\n     * 大端模式将int型转成3个字节的Byte\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] longToByte3BigEndian(long i) {\n        byte[] res = new byte[3];\n        res[2] = (byte) (i & 0xffL);\n        res[1] = (byte) ((i >> 8) & 0xffL);\n        res[0] = (byte) ((i >> 16) & 0xffL);\n        return res;\n    }\n\n    /**\n     * 比较两个byte数组\n     *\n     * @param first\n     * @param second\n     * @return\n     */\n    public static boolean compareBytes(byte[] first, byte[] second) {\n        for (int i = 0; i < (first.length < second.length ? first.length : second.length); i++) {\n            if (first[i] != second[i]) {\n                return false;\n            }\n        }\n        return true && first.length == second.length;\n    }\n\n    /**\n     * 缺失部分有效数字\n     * UUID型转成4字节byte 大端模式\n     *\n     * @param u\n     * @return\n     */\n    public static byte[] uuidToByte4BigEndian(UUID u) {\n        long l = u.getLeastSignificantBits();\n        byte[] b = new byte[4];\n        b[3] = (byte) (l & 0xffL);\n        b[2] = (byte) ((l >> 8) & 0xff00L);\n        b[1] = (byte) ((l >> 16) & 0xff0000L);\n        b[0] = (byte) ((l >> 24) & 0xff000000L);\n        return b;\n    }\n\n    /**\n     * 4字节byte转化成UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte4ToUuidBigEndian(byte[] b) {\n        long l = (long) b[3] & 0xffL;\n        l |= ((long) b[2] << 8) & 0xff00L;\n        l |= ((long) b[1] << 16) & 0xff0000L;\n        l |= ((long) b[0] << 24) & 0xff000000L;\n        return new UUID(0, l);\n    }\n\n\n    /**\n     * 4字节byte转化成UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte4ToUuidBigEndian2(byte[] b) {\n        long l = ((long) b[3] << 32) & 0xff00000000L;\n        l |= ((long) b[2] << 40) & 0xff0000000000L;\n        l |= ((long) b[1] << 48) & 0xff000000000000L;\n        l |= ((long) b[0] << 56) & 0xff00000000000000L;\n        return new UUID(l, 0);\n    }\n\n    /**\n     * 缺失部分有效数字\n     * long型转成4字节byte 大端模式\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToByte4BigEndian(long l) {\n        byte[] b = new byte[4];\n        b[3] = (byte) (l & 0xffL);\n        b[2] = (byte) ((l >> 8) & 0xff00L);\n        b[1] = (byte) ((l >> 16) & 0xff0000L);\n        b[0] = (byte) ((l >> 24) & 0xff000000L);\n        return b;\n    }\n\n    /**\n     * 4字节byte转化成Long 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static long byte4ToLongBigEndian(byte[] b) {\n        long l = (long) b[3] & 0xffL;\n        l |= ((long) b[2] << 8) & 0xff00L;\n        l |= ((long) b[1] << 16) & 0xff0000L;\n        l |= ((long) b[0] << 24) & 0xff000000L;\n        return l;\n    }\n\n    /**\n     * 16个字节转化UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte16ToUuidBigEndian(byte[] b) {\n        return new UUID(byte8ToLongBigEndian(subBytes(b, 0, 8)), byte8ToLongBigEndian(subBytes(b, 8, 8)));\n    }\n\n    /**\n     * UUID转化成16个字节 大端模式\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte16BigEndian(UUID uuid) {\n        long l = uuid.getLeastSignificantBits();\n        long l2 = uuid.getMostSignificantBits();\n        return combineByteArray(longToByte8BigEndian(l2), longToByte8BigEndian(l));\n    }\n\n    /**\n     * 8个字节转化UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte8ToUuidBigEndian(byte[] b) {\n        return new UUID(0, byte8ToLongBigEndian(b));\n    }\n\n    /**\n     * UUID转化成8个字节 大端模式\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte8BigEndian(UUID uuid) {\n        long l = uuid.getLeastSignificantBits();\n        return longToByte8BigEndian(l);\n    }\n\n    /**\n     * 8个字节转化成long型 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static long byte8ToLongBigEndian(byte[] b) {\n        long l = (long) b[7] & 0xffL;\n        l |= ((long) b[6] << 8) & 0xff00L;\n        l |= ((long) b[5] << 16) & 0xff0000L;\n        l |= ((long) b[4] << 24) & 0xff000000L;\n        l |= ((long) b[3] << 32) & 0xff00000000L;\n        l |= ((long) b[2] << 40) & 0xff0000000000L;\n        l |= ((long) b[1] << 48) & 0xff000000000000L;\n        l |= ((long) b[0] << 56) & 0xff00000000000000L;\n        return l;\n    }\n\n    /**\n     * long型转成8个字节 大端模式\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToByte8BigEndian(long l) {\n        byte[] b = new byte[8];\n        b[7] = (byte) (l & 0xffL);\n        b[6] = (byte) ((l >> 8) & 0xffL);\n        b[5] = (byte) ((l >> 16) & 0xffL);\n        b[4] = (byte) ((l >> 24) & 0xffL);\n        b[3] = (byte) ((l >> 32) & 0xffL);\n        b[2] = (byte) ((l >> 40) & 0xffL);\n        b[1] = (byte) ((l >> 48) & 0xffL);\n        b[0] = (byte) ((l >> 56) & 0xffL);\n        return b;\n    }\n\n    /**\n     * 六个字节的byte转化成UUID，有效数字在前，于后补零\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte6ToUuidBigEndian2(byte[] b) {\n        long l = ((long) b[5] << 16) & 0xff0000L;\n        l |= ((long) b[4] << 24) & 0xff000000L;\n        l |= ((long) b[3] << 32) & 0xff00000000L;\n        l |= ((long) b[2] << 40) & 0xff0000000000L;\n        l |= ((long) b[1] << 48) & 0xff000000000000L;\n        l |= ((long) b[0] << 56) & 0xff00000000000000L;\n        return new UUID(l, 0);\n    }\n\n    /**\n     * 将UUID转成六个字节的Byte，有效数字在前，于后补零\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte6BigEndian2(UUID uuid) {\n        long l = uuid.getMostSignificantBits();\n        byte[] b = new byte[6];\n        b[5] = (byte) ((l >> 16) & 0xffL);\n        b[4] = (byte) ((l >> 24) & 0xffL);\n        b[3] = (byte) ((l >> 32) & 0xffL);\n        b[2] = (byte) ((l >> 40) & 0xffL);\n        b[1] = (byte) ((l >> 48) & 0xffL);\n        b[0] = (byte) ((l >> 56) & 0xffL);\n        return b;\n    }\n\n    /**\n     * 六个字节的byte转化成UUID\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte6ToUuidBigEndian(byte[] b) {\n        long l = (long) b[5] & 0xffL;\n        l |= ((long) b[4] << 8) & 0xff00L;\n        l |= ((long) b[3] << 16) & 0xff0000L;\n        l |= ((long) b[2] << 24) & 0xff000000L;\n        l |= ((long) b[1] << 32) & 0xff00000000L;\n        l |= ((long) b[0] << 40) & 0xff0000000000L;\n        return new UUID(0, l);\n    }\n\n    /**\n     * 将UUID转成六个字节的Byte\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte6BigEndian(UUID uuid) {\n        long l = uuid.getLeastSignificantBits();\n        byte[] b = new byte[6];\n        b[5] = (byte) (l & 0xffL);\n        b[4] = (byte) ((l >> 8) & 0xffL);\n        b[3] = (byte) ((l >> 16) & 0xffL);\n        b[2] = (byte) ((l >> 24) & 0xffL);\n        b[1] = (byte) ((l >> 32) & 0xffL);\n        b[0] = (byte) ((l >> 40) & 0xffL);\n        return b;\n    }\n\n    /**\n     * 大端模式将4个字节的Byte转成int型\n     *\n     * @param b\n     * @return\n     */\n    public static int byte4ToIntBigEndian(byte[] b) {\n        int i = b[3] & 0xff;\n        i |= ((b[2] << 8) & 0xff00);\n        i |= ((b[1] << 16) & 0xff0000);\n        i |= ((b[0] << 24) & 0xff000000);\n        return i;\n    }\n\n    /**\n     * 大端模式将int型转成4个字节的Byte\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToByte4BigEndian(int i) {\n        byte[] res = new byte[4];\n        res[3] = (byte) (i & 0xff);\n        res[2] = (byte) ((i >> 8) & 0xff);\n        res[1] = (byte) ((i >> 16) & 0xff);\n        res[0] = (byte) ((i >> 24) & 0xff);\n        return res;\n    }\n\n    /**\n     * 大端模式将3个字节的Byte转成int型\n     *\n     * @param b\n     * @return\n     */\n    public static int byte3ToIntBigEndian(byte[] b) {\n        int i = b[2] & 0xff;\n        i |= ((b[1] << 8) & 0xff00);\n        i |= ((b[1] << 16) & 0xff0000);\n        return i;\n    }\n\n    /**\n     * 大端模式将int型转成3个字节的Byte\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToByte3BigEndian(int i) {\n        byte[] res = new byte[3];\n        res[2] = (byte) (i & 0xff);\n        res[1] = (byte) ((i >> 8) & 0xff);\n        res[0] = (byte) ((i >> 16) & 0xff);\n        return res;\n    }\n\n    public static String toString(byte[] data) {\n        return toString(data, \" \");\n    }\n\n    public static String toString(byte[] data, String interval) {\n        if (data == null) {\n            return null;\n        }\n        String iStr = interval == null ? \"\" : interval;\n        StringBuilder buffer = new StringBuilder();\n        for (byte b : data) {\n            String d = Integer.toHexString(b & 0xff);\n            if (d.length() > 1) {\n                buffer.append(d).append(iStr);\n            } else {\n                buffer.append(\"0\").append(d).append(iStr);\n            }\n        }\n        return buffer.toString();\n    }\n\n    public static byte[] subBytes(byte[] data, int start, int len) {\n        byte[] res = new byte[len];\n        for (int i = 0; i < len; i++) {\n            res[i] = data[start + i];\n        }\n        return res;\n    }\n\n    public static byte[] subBytes(byte[] data, int start) {\n        int len = data.length - start;\n        return subBytes(data, start, len);\n    }\n\n    /**\n     * 将字节数组转化成Long\n     *\n     * @param b\n     * @return\n     */\n    public static long bytesToLong(byte[] b) {\n        long l = ((long) b[0] << 56) & 0xFF00000000000000L;\n        // 如果不强制转换为long，那么默认会当作int，导致最高32位丢失\n        l |= ((long) b[1] << 48) & 0xFF000000000000L;\n        l |= ((long) b[2] << 40) & 0xFF0000000000L;\n        l |= ((long) b[3] << 32) & 0xFF00000000L;\n        l |= ((long) b[4] << 24) & 0xFF000000L;\n        l |= ((long) b[5] << 16) & 0xFF0000L;\n        l |= ((long) b[6] << 8) & 0xFF00L;\n        l |= b[7] & 0xFFL;\n        return l;\n    }\n\n    /**\n     * 将Long转化成字节数组\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToBytes(long l) {\n        byte[] b = new byte[8];\n        b[0] = (byte) (l >>> 56);\n        b[1] = (byte) (l >>> 48);\n        b[2] = (byte) (l >>> 40);\n        b[3] = (byte) (l >>> 32);\n        b[4] = (byte) (l >>> 24);\n        b[5] = (byte) (l >>> 16);\n        b[6] = (byte) (l >>> 8);\n        b[7] = (byte) (l);\n        return b;\n    }\n\n    /**\n     * 合并Byte数组\n     *\n     * @param bytes\n     * @return\n     */\n    public static byte[] combineByteArray(byte[]... bytes) {\n        int len = 0;\n        for (byte[] bs : bytes) {\n            if (bs != null) {\n                len += bs.length;\n            }\n        }\n        byte[] res = new byte[len];\n        int pos = 0;\n        for (byte[] bs : bytes) {\n            if (bs != null) {\n                for (byte b : bs) {\n                    res[pos++] = b;\n                }\n            }\n        }\n        return res;\n    }\n\n    /**\n     * 将UUID转化成Byte数组\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToBytes(UUID uuid) {\n        return combineByteArray(longToBytes(uuid.getMostSignificantBits()), longToBytes(uuid.getLeastSignificantBits()));\n    }\n\n    /**\n     * 将Byte数组转化成UUID\n     *\n     * @param data\n     * @return\n     */\n    public static UUID bytesToUuid(byte[] data) {\n        if (data == null || data.length < 16)\n            return null;\n        byte[] bMost = subBytes(data, 0, 8);\n        byte[] bLeast = subBytes(data, 8, 8);\n        return new UUID(bytesToLong(bMost), bytesToLong(bLeast));\n    }\n\n    /**\n     * 将Int转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytes2(int i) {\n        byte[] res = new byte[2];\n        res[1] = (byte) (i & 0xFF);\n        res[0] = (byte) (i >> 8 & 0xFF);\n        return res;\n    }\n\n    public static byte[] intToBytes2SmallDian(int i) {\n        byte[] res = new byte[2];\n        res[0] = (byte) (i & 0xFF);\n        res[1] = (byte) (i >> 8 & 0xFF);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int,取第一二个字节\n     *\n     * @param b\n     * @return\n     */\n    public static int bytes2ToInt(byte[] b) {\n        int c = (int) ((b[0] << 8) & 0xFF00L);\n        c |= (int) (b[1] & 0xFFL);\n        return c;\n    }\n\n    /**\n     * 将Byte数组转化为int,取第一二个字节,小端模式\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesS2ToInt(byte[] b) {\n        int c = (int) ((b[1] << 8) & 0xFF00L);\n        c |= (int) (b[0] & 0xFFL);\n        return c;\n    }\n\n    /**\n     * 将Byte数组转化为int,取第一三个字节\n     *\n     * @param b\n     * @return\n     */\n    public static int bytes2ToInt2(byte[] b) {\n        int c = (int) ((b[1] << 16) & 0xFFFFFF00L);\n        c |= (int) (b[0]);\n        return c;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToIntBigEndian(byte[] b) {\n        int res = (int) ((b[0] << 24) & 0xFF000000L);\n        res |= (b[1] << 16 & 0xFF0000L);\n        res |= (b[2] << 8 & 0xFF00L);\n        res |= (b[3] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToIntSmallEndian(byte[] b) {\n        int res = (int) ((b[3] << 24) & 0xFF000000L);\n        res |= (b[2] << 16 & 0xFF0000L);\n        res |= (b[1] << 8 & 0xFF00L);\n        res |= (b[0] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Int转化成Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytesBigEndian(int i) {\n        byte[] res = new byte[4];\n        res[0] = (byte) ((i >> 24) & 0xFFL);\n        res[1] = (byte) ((i >> 16) & 0xFFL);\n        res[2] = (byte) ((i >> 8) & 0xFFL);\n        res[3] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToInt(byte[] b) {\n        int res = (int) ((b[3] << 24) & 0xFF000000L);\n        res |= (b[2] << 16 & 0xFF0000L);\n        res |= (b[1] << 8 & 0xFF00L);\n        res |= (b[0] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Int转化成Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytes(int i) {\n        byte[] res = new byte[4];\n        res[3] = (byte) ((i >> 24) & 0xFFL);\n        res[2] = (byte) ((i >> 16) & 0xFFL);\n        res[1] = (byte) ((i >> 8) & 0xFFL);\n        res[0] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化成short\n     *\n     * @param b\n     * @return\n     */\n    public static short bytesToShort(byte[] b) {\n        short res = (short) ((b[1] << 8) & 0xFF00l);\n        res |= (b[0] & 0xFFl);\n        return res;\n    }\n\n    /**\n     * 将Short转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] shortToBytes(short i) {\n        byte[] res = new byte[2];\n        res[1] = (byte) ((i >> 8) & 0xFFL);\n        res[0] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 大端序\n     * 将Byte数组转化成short\n     *\n     * @param b\n     * @return\n     */\n    public static short bytesToShortBigEndian(byte[] b) {\n        short res = (short) ((b[0] << 8) & 0xFF00L);\n        res |= (b[1] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 大端序\n     * 将Short转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] shortToBytesBigEndian(short i) {\n        byte[] res = new byte[2];\n        res[0] = (byte) ((i >> 8) & 0xFFL);\n        res[1] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 填充数据至长度为N的数组\n     *\n     * @param data\n     * @param len\n     * @return\n     */\n    public static byte[] wrapData(byte[] data, int len) {\n        byte[] res = new byte[len];\n        for (int i = 0; i < len; i++) {\n            if (i < data.length) {\n                res[i] = data[i];\n            } else {\n                res[i] = 0;\n            }\n        }\n        return res;\n    }\n\n    /**\n     * 取出一字节中每一位的数值\n     *\n     * @param data\n     * @return\n     */\n    public static byte[] getByteBits(byte data) {\n        byte[] result = new byte[8];\n        for (int i = 0; i < 8; i++) {\n            result[i] = (byte) (data >> i & 0x01);\n        }\n        return result;\n    }\n\n    public static byte[] getBytesWithBuffer(ByteBuffer buffer) {\n        if (buffer == null)\n            return null;\n        byte[] bytes = new byte[buffer.position()];\n        for (int i = 0; i < buffer.position(); i++)\n            bytes[i] = buffer.array()[i];\n        return bytes;\n    }\n\n    public static boolean equal(byte[] data1, byte[] data2) {\n        return compare(data1, data2) == 0;\n    }\n\n    public static int compare(byte[] bytes1, byte[] bytes2) {\n        if (bytes1.length > bytes2.length) {\n            return 1;\n        }\n        if (bytes1.length < bytes2.length) {\n            return -1;\n        }\n\n        for (int i = 0; i < bytes1.length; i++) {\n            int result = (bytes1[i] & 0xFF) - (bytes2[i] & 0xFF);\n            if (result > 0) {\n                return 1;\n            }\n            if (result < 0) {\n                return -1;\n            }\n        }\n        return 0;\n\n    }\n\n    public static byte getXor(byte[] data) {\n        return getXor(data, 0, data.length);\n    }\n\n    public static byte getXor(byte[] data, int start, int len) {\n        byte xor = 0;\n        for (int i = start; i < len + start; i++)\n            xor ^= data[i];\n        return xor;\n    }\n\n    /**\n     * bytes 转Ascii码字符串\n     */\n    public static String bytesToAscii(byte[] bytes, int offset, int dateLen) {\n        if ((bytes == null) || (bytes.length == 0) || (offset < 0) || (dateLen <= 0)) {\n            return null;\n        }\n        if ((offset >= bytes.length) || (bytes.length - offset < dateLen)) {\n            return null;\n        }\n\n        String asciiStr = null;\n        byte[] data = new byte[dateLen];\n        System.arraycopy(bytes, offset, data, 0, dateLen);\n        asciiStr = new String(data, StandardCharsets.US_ASCII);\n        return asciiStr;\n    }\n\n    public static String bytesToAscii(byte[] bytes, int dateLen) {\n        return bytesToAscii(bytes, 0, dateLen);\n    }\n\n    public static String bytesToAscii(byte[] bytes) {\n        return bytesToAscii(bytes, 0, bytes.length);\n    }\n\n    public static byte[] asciiToBytes(String string) {\n        if (string == null) {\n            return null;\n        }\n        return string.getBytes(StandardCharsets.US_ASCII);\n    }\n\n    /**\n     * 16 进制字符串转bytes数据\n     */\n    public static byte[] hexStringToBytes(@NonNull String hexString) {\n        byte[] result = new byte[hexString.length() / 2];\n        for (int i = 0; i < hexString.length() / 2; ++i) {\n            String temp = hexString.substring(2 * i, 2 * (i + 1));\n            result[i] = (byte) Integer.parseInt(temp, 16);\n        }\n        return result;\n    }\n\n    public static String addSpaceToString(String origin, int strLength) {\n        if (origin == null) {\n            origin = \"\";\n        }\n        String result = origin;\n        if (origin.length() < strLength) {\n            StringBuilder sb = new StringBuilder();\n            sb.append(origin);// 左补0\n            while (sb.length() < strLength) {\n                sb.append(\" \");\n            }\n            result = sb.toString();\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/DateUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * 日期工具类\n *\n * @author jingle1267@163.com\n */\npublic final class DateUtils {\n\n    /**\n     * 日期类型 *\n     */\n    public static final String yyyyMMDD = \"yyyy-MM-dd\";\n    public static final String yyyyMMddHHmmss = \"yyyy-MM-dd HH:mm:ss\";\n    public static final String HH_mm_ss = \"HH:mm:ss\";\n    public static final String HHmmss = \"HHmmss\";\n    public static final String yyMMddHHmm = \"yyMMddHHmm\";\n    public static final String ddHHmmss = \"ddHHmmss\";\n    public static final String hhmmss = \"HH:mm:ss\";\n    public static final String HH_mm_ss_SSS = \"HH:mm:ss.SSS\";\n    public static final String LOCALE_DATE_FORMAT = \"yyyy年M月d日 HH:mm:ss\";\n    public static final String DB_DATA_FORMAT = \"yyyy-MM-dd HH:mm:ss\";\n    public static final String NEWS_ITEM_DATE_FORMAT = \"hh:mm M月d日 yyyy\";\n    public static final String COMMON_TYPE = \"yyyyMMddHHmmss\";\n    public static final String MMddHHmmss = \"MM月dd日HH时mm分ss秒\";\n    public static final long SECOND = 1000;//1000毫秒\n    public static final long MINUTE = 60 * SECOND;\n    public static final long HOUR = 60 * MINUTE;\n    public static final long DAY = 24 * HOUR;\n\n    public static String getCurrentDateString() {\n    \treturn formatDate(new Date(), yyyyMMddHHmmss);\n    }\n\n    /**\n     * 在原基础上加N个小时\n     *\n     * @param date\n     * @param hour\n     * @return\n     */\n    public static Date dateAddHours(Date date, int hour) {\n        long timestamp = date.getTime() + hour * 60 * 60 * 1000;\n        return new Date(timestamp);\n    }\n\n    public static String dateToString(long time, String pattern)\n            throws Exception {\n        return dateToString(new Date(time), pattern);\n    }\n\n    public static String dateToString(Date date, String pattern)\n            throws Exception {\n        return new SimpleDateFormat(pattern).format(date);\n    }\n\n    public static Date stringToDate(String dateStr, String pattern)\n            throws Exception {\n        return new SimpleDateFormat(pattern).parse(dateStr);\n    }\n\n    public static String formatDate(long time, String type) {\n        return formatDate(new Date(time), type);\n    }\n\n    public static String formatDate(Date date) {\n    \treturn formatDate(date, yyyyMMddHHmmss);\n    }\n    /**\n     * 将Date类型转换为日期字符串\n     *\n     * @param date Date对象\n     * @param type 需要的日期格式\n     * @return 按照需求格式的日期字符串\n     */\n    public static String formatDate(Date date, String type) {\n        try {\n            SimpleDateFormat df = new SimpleDateFormat(type);\n            return df.format(date);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * 将日期字符串转换为Date类型\n     *\n     * @param dateStr 日期字符串\n     * @param type    日期字符串格式\n     * @return Date对象\n     */\n    public static Date parseDate(String dateStr, String type) {\n        SimpleDateFormat df = new SimpleDateFormat(type);\n        Date date = null;\n        try {\n            date = df.parse(dateStr);\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return date;\n\n    }\n\n    /**\n     * 得到年\n     *\n     * @param date Date对象\n     * @return 年\n     */\n    public static int getYear(Date date) {\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.YEAR);\n    }\n\n\n    /**\n     * 得到月\n     *\n     * @param date Date对象\n     * @return 月\n     */\n    public static int getMonth(Date date) {\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.MONTH) + 1;\n\n    }\n\n    /**\n     * 得到日\n     *\n     * @param date Date对象\n     * @return 日\n     */\n    public static int getDay(Date date) {\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.DAY_OF_MONTH);\n    }\n\n\n\n    public static int getHour(Date date){\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.HOUR_OF_DAY);\n    }\n\n    public static int getMinute(Date date){\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.MINUTE);\n    }\n\n    public static int getSecond(Date date){\n        Calendar c = Calendar.getInstance();\n        c.setTime(date);\n        return c.get(Calendar.SECOND);\n    }\n\n\n    /**\n     * 转换日期 将日期转为今天, 昨天, 前天, XXXX-XX-XX, ...\n     *\n     * @param time 时间\n     * @return 当前日期转换为更容易理解的方式\n     */\n    public static String translateDate(Long time) {\n        long oneDay = 24 * 60 * 60 * 1000;\n        Calendar current = Calendar.getInstance();\n        Calendar today = Calendar.getInstance();    //今天\n\n        today.set(Calendar.YEAR, current.get(Calendar.YEAR));\n        today.set(Calendar.MONTH, current.get(Calendar.MONTH));\n        today.set(Calendar.DAY_OF_MONTH, current.get(Calendar.DAY_OF_MONTH));\n        //  Calendar.HOUR——12小时制的小时数 Calendar.HOUR_OF_DAY——24小时制的小时数\n        today.set(Calendar.HOUR_OF_DAY, 0);\n        today.set(Calendar.MINUTE, 0);\n        today.set(Calendar.SECOND, 0);\n\n        long todayStartTime = today.getTimeInMillis();\n\n        if (time >= todayStartTime && time < todayStartTime + oneDay) { // today\n            return \"今天\";\n        } else if (time >= todayStartTime - oneDay && time < todayStartTime) { // yesterday\n            return \"昨天\";\n        } else if (time >= todayStartTime - oneDay * 2 && time < todayStartTime - oneDay) { // the day before yesterday\n            return \"前天\";\n        } else if (time > todayStartTime + oneDay) { // future\n            return \"将来某一天\";\n        } else {\n            SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n            Date date = new Date(time);\n            return dateFormat.format(date);\n        }\n    }\n\n    /**\n     * 转换日期 转换为更为人性化的时间\n     *\n     * @param time 时间\n     * @return\n     */\n    private String translateDate(long time, long curTime) {\n        long oneDay = 24 * 60 * 60;\n        Calendar today = Calendar.getInstance();    //今天\n        today.setTimeInMillis(curTime * 1000);\n        today.set(Calendar.HOUR_OF_DAY, 0);\n        today.set(Calendar.MINUTE, 0);\n        today.set(Calendar.SECOND, 0);\n        long todayStartTime = today.getTimeInMillis() / 1000;\n        if (time >= todayStartTime) {\n            long d = curTime - time;\n            if (d <= 60) {\n                return \"1分钟前\";\n            } else if (d <= 60 * 60) {\n                long m = d / 60;\n                if (m <= 0) {\n                    m = 1;\n                }\n                return m + \"分钟前\";\n            } else {\n                SimpleDateFormat dateFormat = new SimpleDateFormat(\"今天 HH:mm\");\n                Date date = new Date(time * 1000);\n                String dateStr = dateFormat.format(date);\n                if (!TextUtils.isEmpty(dateStr) && dateStr.contains(\" 0\")) {\n                    dateStr = dateStr.replace(\" 0\", \" \");\n                }\n                return dateStr;\n            }\n        } else {\n            if (time < todayStartTime && time > todayStartTime - oneDay) {\n                SimpleDateFormat dateFormat = new SimpleDateFormat(\"昨天 HH:mm\");\n                Date date = new Date(time * 1000);\n                String dateStr = dateFormat.format(date);\n                if (!TextUtils.isEmpty(dateStr) && dateStr.contains(\" 0\")) {\n\n                    dateStr = dateStr.replace(\" 0\", \" \");\n                }\n                return dateStr;\n            } else if (time < todayStartTime - oneDay && time > todayStartTime - 2 * oneDay) {\n                SimpleDateFormat dateFormat = new SimpleDateFormat(\"前天 HH:mm\");\n                Date date = new Date(time * 1000);\n                String dateStr = dateFormat.format(date);\n                if (!TextUtils.isEmpty(dateStr) && dateStr.contains(\" 0\")) {\n                    dateStr = dateStr.replace(\" 0\", \" \");\n                }\n                return dateStr;\n            } else {\n                SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n                Date date = new Date(time * 1000);\n                String dateStr = dateFormat.format(date);\n                if (!TextUtils.isEmpty(dateStr) && dateStr.contains(\" 0\")) {\n                    dateStr = dateStr.replace(\" 0\", \" \");\n                }\n                return dateStr;\n            }\n        }\n    }\n\n    /**\n     * 计算剩余时间\n     *\n     * @param time\n     * @param curTime\n     * @return\n     */\n    public static String leftTime(long time, long curTime) {\n        StringBuffer buf = new StringBuffer();\n        long leftTime = time - curTime;\n        if (leftTime < 0) {\n            buf.append(\"Expired\");\n        } else if (leftTime >= 0 && leftTime < 1000) {\n            buf.append(\"Expired\");\n        } else {\n            if (leftTime / DAY > 0) {\n                buf.append((leftTime / DAY) + \"Day(s)\");\n                leftTime %= DAY;\n            }\n            if (leftTime / HOUR > 0) {\n                buf.append((leftTime / HOUR) + \"Hour(s)\");\n                leftTime %= HOUR;\n            }\n            if (leftTime / MINUTE > 0) {\n                buf.append((leftTime / MINUTE) + \"Minute(s)\");\n                leftTime %= MINUTE;\n            }\n            if (leftTime / SECOND > 0) {\n                buf.append((leftTime / SECOND) + \"Second(s)\");\n                leftTime %= SECOND;\n            }\n        }\n        return buf.toString();\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/DialogUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.content.DialogInterface;\nimport android.os.Handler;\nimport android.view.View;\n\nimport com.luo.bluetooth.R;\nimport com.luo.bluetooth.customview.searchble.Effectstype;\nimport com.luo.bluetooth.customview.searchble.NiftyDialogBuilder;\n\nimport cn.pedant.SweetAlert.SweetAlertDialog;\n\n\n/**\n * Created by Bruce on 2017/4/12 0012.\n */\npublic class DialogUtils {\n    private static final short DIALOG_SHOW_TIME_SHORT = 1500;\n    private static final short DIALOG_SHOW_TIME_LONG = 2500;\n    private static NiftyDialogBuilder dialogBuilder;\n    private static SweetAlertDialog dialog;\n\n    public static void showProgressbarDialog(Context context) {\n        showProgressbarDialog(context, null);\n    }\n\n    public static void showProgressbarDialog(Context context, int titleId) {\n        showProgressbarDialog(context, context.getString(titleId));\n    }\n\n    public static void showProgressbarDialog(Context context, String title) {\n        if (!checkReuseable(context, SweetAlertDialog.PROGRESS_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.PROGRESS_TYPE);\n        }\n        dialog.setTitleText(title);\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.show();\n    }\n\n\n    public static void showNormalDialog(Context context, int text) {\n        showNormalDialog(context, context.getString(text));\n    }\n\n    public static void showNormalDialog(Context context, String text) {\n        showNormalDialog(context, text, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showNormalDialog(Context context, String text, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setContentText(text)\n                .setTitleText(null)\n                .setConfirmClickListener(listener)\n                .show();\n    }\n\n    public static SweetAlertDialog showNormalDialog(Context context, String text,\n                                                    SweetAlertDialog.OnSweetClickListener listener,\n                                                    SweetAlertDialog.OnSweetClickListener cancelListener) {\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setContentText(text)\n                .setTitleText(null)\n                .setConfirmClickListener(listener)\n                .setCancelClickListener(cancelListener)\n                .show();\n        return dialog;\n    }\n\n    public static void showNormalDialog(Context context, String title, String text, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setContentText(text)\n                .setTitleText(title)\n                .setConfirmClickListener(listener)\n                .show();\n    }\n\n    public static SweetAlertDialog showNormalDialog(Context context, String title, String text,\n                                                    String confirmText,\n                                                    SweetAlertDialog.OnSweetClickListener listener,\n                                                    String cancelText,\n                                                    SweetAlertDialog.OnSweetClickListener cancelListener) {\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setContentText(text)\n                .setTitleText(title)\n                .setConfirmText(confirmText)\n                .setConfirmClickListener(listener)\n                .setCancelText(cancelText)\n                .setCancelClickListener(cancelListener)\n                .show();\n        return dialog;\n    }\n\n    public static SweetAlertDialog showNormalDialog(Context context, String title,\n                                                    String confirmText,\n                                                    SweetAlertDialog.OnSweetClickListener listener,\n                                                    String cancelText,\n                                                    SweetAlertDialog.OnSweetClickListener cancelListener) {\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setTitleText(title)\n                .setConfirmText(confirmText)\n                .setConfirmClickListener(listener)\n                .setCancelText(cancelText)\n                .setCancelClickListener(cancelListener)\n                .show();\n        return dialog;\n    }\n\n    public static void showLongErrorDialog(final Context context, String title) {\n        showLongErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showLongErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        showErrorDialog(context, title, DIALOG_SHOW_TIME_LONG, listener);\n    }\n\n    public static void showShortErrorDialog(final Context context, int title) {\n        showShortErrorDialog(context, context.getString(title));\n    }\n\n    public static void showShortErrorDialog(final Context context, String title) {\n        showShortErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showShortErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        showErrorDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);\n    }\n\n    public static void showErrorDialog(Context context, int title) {\n        showErrorDialog(context, context.getString(title));\n    }\n\n    public static void showErrorDialog(Context context, String title) {\n        showErrorDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showErrorDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {\n        showErrorDialog(context, title, listener);\n        new Handler().postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                dismissDialog(true);\n            }\n        }, duration);\n    }\n\n    public static void showErrorDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.ERROR_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.ERROR_TYPE);\n        }\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.setTitleText(title).setConfirmClickListener(listener);\n        dialog.show();\n    }\n\n    public static void showErrorDialog(Context context, String title,String content, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.ERROR_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.ERROR_TYPE);\n        }\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.setTitleText(title).setContentText(content).setConfirmClickListener(listener);\n        dialog.show();\n    }\n\n    public static void showShortSuccessDialog(Context context, int title) {\n        showShortSuccessDialog(context, context.getString(title));\n    }\n\n    public static void showShortSuccessDialog(Context context, String title) {\n        showShortSuccessDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showShortSuccessDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        showSuccessDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);\n    }\n\n    public static void showSuccessDialog(Context context, int title) {\n        showSuccessDialog(context, context.getString(title));\n    }\n\n    public static void showSuccessDialog(Context context, String title) {\n        showSuccessDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showSuccessDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {\n        showSuccessDialog(context, title, listener);\n        new Handler().postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                dismissDialog(true);\n            }\n        }, duration);\n    }\n\n    public static void showSuccessDialog(Context context, int title, SweetAlertDialog.OnSweetClickListener listener) {\n        showSuccessDialog(context, context.getString(title), listener);\n    }\n\n    public static void showSuccessDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.SUCCESS_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.SUCCESS_TYPE);\n        }\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.setTitleText(title)\n                .setConfirmClickListener(listener)\n                .show();\n    }\n\n    public static void showSuccessDialog(Context context, String title, String content, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.SUCCESS_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.SUCCESS_TYPE);\n        }\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.setTitleText(title)\n                .setContentText(content)\n                .setConfirmClickListener(listener)\n                .show();\n    }\n\n    public static void showShortWarningDialog(Context context, int title) {\n        showShortWarningDialog(context, context.getString(title));\n    }\n\n    public static void showShortWarningDialog(Context context, String title) {\n        showShortWarningDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showShortWarningDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        showWarningDialog(context, title, DIALOG_SHOW_TIME_SHORT, listener);\n    }\n\n    public static void showWarningDialog(Context context, int id) {\n        showWarningDialog(context, context.getString(id));\n    }\n\n    public static void showWarningDialog(Context context, String title) {\n        showWarningDialog(context, title, new SweetAlertDialog.OnSweetClickListener() {\n            @Override\n            public void onClick(SweetAlertDialog sweetAlertDialog) {\n                dismissDialog(true);\n            }\n        });\n    }\n\n    public static void showWarningDialog(Context context, String title, short duration, SweetAlertDialog.OnSweetClickListener listener) {\n        showWarningDialog(context, title, listener);\n        new Handler().postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                dismissDialog(true);\n            }\n        }, duration);\n    }\n\n    public static void showWarningDialog(Context context, String title, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);\n        }\n        dialog.setTitleText(title).setConfirmClickListener(listener);\n        dialog.show();\n    }\n\n    public static void showWarningDialog(Context context, String title,String content, SweetAlertDialog.OnSweetClickListener listener) {\n        if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);\n        }\n        dialog.setTitleText(title).setContentText(content).setConfirmClickListener(listener);\n        dialog.show();\n    }\n\n    public static void showWarningDialog(Context context, String title,String content, String confirmText,SweetAlertDialog.OnSweetClickListener confirmListener,\n                                         String cancelText, SweetAlertDialog.OnSweetClickListener cancelListener) {\n        if (!checkReuseable(context, SweetAlertDialog.WARNING_TYPE)){\n            dialog = new SweetAlertDialog(context, SweetAlertDialog.WARNING_TYPE);\n        }\n        dialog.setTitleText(title)\n                .setContentText(content)\n                .setConfirmText(confirmText)\n                .setConfirmClickListener(confirmListener)\n                .setCancelText(cancelText)\n                .setCancelClickListener(cancelListener);\n        dialog.show();\n    }\n\n    public static SweetAlertDialog showCancelableDialog(Context context, String text, SweetAlertDialog.OnSweetClickListener listener){\n        if (!checkReuseable(context, SweetAlertDialog.NORMAL_TYPE)){\n            dialog = new SweetAlertDialog(context,SweetAlertDialog.NORMAL_TYPE);\n        }\n        dialog.setCancelText(\"取消\")\n                .setConfirmText(\"确认\")\n                .setContentText(text)\n                .setTitleText(null)\n                .setCancelClickListener(listener);\n        dialog.setCancelable(false);\n        dialog.setCanceledOnTouchOutside(false);\n        dialog.show();\n        return dialog;\n    }\n\n    private static boolean checkReuseable(Context context, int dialogType){\n        if (dialog==null)\n            return false;\n        if (!isMatchingCurrentContext(context) || dialog.getAlerType() != dialogType){\n            dialog.dismiss();\n            dialog = null;\n            return false;\n        }\n        return true;\n    }\n\n    public static boolean isMatchingCurrentContext(Context context){\n        return ((ContextWrapper)dialog.getContext()).getBaseContext() == context;\n    }\n\n    public static Dialog getDialog(){\n        return dialog;\n    }\n\n    public static void dismissDialog() {\n        dismissDialog(false);\n    }\n\n    public static void dismissDialog(boolean animate) {\n        if (dialog!=null){\n            if (animate)\n                dialog.cancel();\n            else\n                dialog.dismiss();\n            dialog = null;\n        }\n    }\n\n    public static void dismissMatchCurrentDialog(Context context){\n        if (dialog!=null && isMatchingCurrentContext(context))\n            dismissDialog(false);\n    }\n\n    /******************************************NiftyDialogBuilder************************************************/\n\n    /**\n     * 弹出提示框\n     *\n     * @param titleId\n     * @param contentId\n     */\n    public static void showDialog(Context context, int titleId, int contentId) {\n        String title = context.getString(titleId);\n        String content = context.getString(contentId);\n        showNiftyTipDialog(context, title, content);\n    }\n\n    public static void showNiftyTipDialog(Context context, String title, String content) {\n        if (dialogBuilder == null) {\n            dialogBuilder = NiftyDialogBuilder.getInstance(context);\n        }\n        dialogBuilder.withTitle(title)//.withTitle(null)  no title\n                .removeAllCustomView()\n                .withEffect(Effectstype.values()[9])\n                .withTitleColor(\"#FFFFFF\")                                  //def\n                .withDividerColor(\"#11000000\")                              //def\n                .withMessage(content)                     //.withMessage(null)  no Msg\n                .withMessageColor(\"#FFFFFFFF\")                              //def  | withMessageColor(int resid)\n                .withDialogColor(\"#33ffff\")                               //def  | withDialogColor(int resid)\n//                .withIcon(ContextCompat.getDrawable(context, R.mipmap.icon_staginfo))\n                .withDuration(700)                                          //def\n                //   .withEffect(effect)                                         //def Effectstype.Slidetop\n                .withButton1Text(context.getString(R.string.btn_ok))                                    //def gone\n                .isCancelableOnTouchOutside(true)                           //def    | isCancelable(true)\n                //  .setCustomView(R.layout.custom_view,v.getContext())         //.setCustomView(View or ResId,context)\n                .setButton1Click(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        dialogBuilder.dismiss();\n                    }\n                })\n                .show();\n    }\n\n    public static void showNiftyCustonDialog(Context context, int title, View view) {\n        showNiftyCustonDialog(context, context.getString(title), view);\n    }\n\n    public static void showNiftyCustonDialog(Context context, String title, View view) {\n        showNiftyCustonDialog(context, title, view, null);\n    }\n\n    public static void showNiftyCustonDialog(Context context, String title, View view, DialogInterface.OnDismissListener listener) {\n        dialogBuilder = NiftyDialogBuilder.getInstance(context);\n        dialogBuilder.withTitle(title)//.withTitle(null)  no title\n                .setCustomView(view, context)\n                .withEffect(Effectstype.values()[9])\n                .withTitleColor(\"#FFFFFF\")                                  //def\n                .withDividerColor(\"#FFFFFF\")                              //def\n                .withMessage(null)                     //.withMessage(null)  no Msg\n                .withMessageColor(\"#FFFFFF\")                              //def  | withMessageColor(int resid)\n                .withDialogColor(\"#1fbaf3\")                               //def  | withDialogColor(int resid)\n//                .withIcon(ContextCompat.getDrawable(context, R.mipmap.icon_staginfo))\n                .withDuration(700)                                          //def\n                //   .withEffect(effect)                                         //def Effectstype.Slidetop\n                .withButton1Text(context.getString(R.string.btn_cancel))                                    //def gone\n                .isCancelableOnTouchOutside(false)                           //def    | isCancelable(true)\n                //  .setCustomView(R.layout.custom_view,v.getContext())         //.setCustomView(View or ResId,context)\n                .setButton1Click(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        dialogBuilder.dismiss();\n                    }\n                })\n                .setOnDismissListener(listener);\n        dialogBuilder.show();\n    }\n\n    public static void dismissNiftyDialog() {\n        if (dialogBuilder != null) {\n            dialogBuilder.dismiss();\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/GpsUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport android.content.Context;\nimport android.location.LocationManager;\n\npublic class GpsUtils {\n    public static boolean isOPen(final Context context) {\n        LocationManager locationManager\n                = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);\n\n        // 通过GPS卫星定位，定位级别可以精确到街（通过24颗卫星定位，在室外和空旷的地方定位准确、速度快）\n        boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);\n\n        // 通过WLAN或移动网络(3G/2G)确定的位置（也称作AGPS，辅助GPS定位。主要用于在室内或遮盖物（建筑群或茂密的深林等）密集的地方定位）\n        boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);\n        if (gps || network) {\n            return true;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/GsonUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonParser;\n\nimport java.text.SimpleDateFormat;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * Created by tony.luopeiqin on 2017/9/25.\n */\n\npublic class GsonUtils {\n\n\tpublic static boolean isPrintException = true;\n\n\tprivate static Gson gson = new GsonBuilder()\n\t\t\t  .setDateFormat(\"yyyy-MM-dd HH:mm:ss\")\n\t\t\t  .create();  ;\n\n\tprivate GsonUtils() {\n\t\tthrow new Error(\"Do not need instantiate!\");\n\t}\n\n\tprivate static final SimpleDateFormat dateFormate = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\n\t/**\n\t * Json数组转对象链表\n\t *\n\t * @param jsonArray\n\t * @param cls\n\t * @param <T>\n\t * @return\n\t */\n\tpublic static <T> List<T> string2Objects(String jsonArray, Class<T> cls) {\n\t\tList<T> res = new LinkedList<T>();\n\t\t//Json的解析类对象\n\t    JsonParser parser = new JsonParser();\n\t    //将JSON的String 转成一个JsonArray对象\n\t    JsonArray array = parser.parse(jsonArray).getAsJsonArray();\n\t\tfor (int i = 0; i < array.size(); i++) {\n\t\t\tT t = gson.fromJson(array.get(i).toString(), cls);\n\t\t\t// T t= JSONUtils.string2Obejct(array.get(i).toString(),cls);\n\t\t\tres.add(t);\n\t\t}\n\t\treturn res;\n\t}\n\n\t/**\n\t * 从java对象转换为字符串\n\t */\n\tpublic static String Object2String(Object obj) {\n\t\treturn gson.toJson(obj);\n\t}\n\n\t/**\n\t * 从字符串转换为java对象\n\t */\n\tpublic static <T> T string2Object(String jsonStr, Class<T> c) {\n\t\tif (jsonStr.startsWith(\"[\") && jsonStr.endsWith(\"]\")) {\n\t\t\tjsonStr = jsonStr.replace('[', '{').replace(']', '}');\n\t\t}\n\t\tT t = gson.fromJson(jsonStr, c);\n\t\treturn t;\n\t}\n\n\tpublic static String objToJson(Object obj) {\n\t\treturn gson.toJson(obj);\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/LogUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport android.util.Log;\n\nimport com.tencent.bugly.crashreport.BuglyLog;\nimport com.luo.bluetooth.BuildConfig;\n\npublic class LogUtils {\n\n    private static boolean isDebug = BuildConfig.DEBUG;\n\n    private LogUtils() {\n    }\n\n    public static void v(String tag, String msg) {\n        BuglyLog.v(tag, msg);\n        if (isDebug) {\n            Log.v(tag, msg);\n        }\n    }\n\n    public static void d(String tag, String msg) {\n        BuglyLog.d(tag, msg);\n        if (isDebug) {\n            //信息太长,分段打印\n            //因为String的length是字符数量不是字节数量所以为了防止中文字符过多，\n            //  把4*1024的MAX字节打印长度改为2001字符数\n            int max_str_length = 2001 - tag.length();\n            //大于4000时\n            while (msg.length() > max_str_length) {\n                Log.d(tag, msg.substring(0, max_str_length));\n                msg = msg.substring(max_str_length);\n            }\n            //剩余部分\n            Log.d(tag, msg);\n        }\n    }\n\n    public static void i(String tag, String msg) {\n        BuglyLog.i(tag, msg);\n        if (isDebug) {\n            Log.i(tag, msg);\n        }\n    }\n\n    public static void w(String tag, String msg) {\n        BuglyLog.w(tag, msg);\n        if (isDebug) {\n            Log.w(tag, msg);\n        }\n    }\n\n    public static void e(String tag, String msg) {\n        BuglyLog.e(tag, msg);\n        if (isDebug) {\n            Log.e(tag, msg);\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/TextUtils.java",
    "content": "package com.luo.bluetooth.utils;\n\npublic class TextUtils {\n\n\tpublic static boolean isEmpty(String dateStr) {\n\t\t// TODO Auto-generated method stub\n\t\tif (dateStr != null && dateStr.length() > 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/luo/bluetooth/utils/ToastUtil.java",
    "content": "package com.luo.bluetooth.utils;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.Toast;\n\n/**\n * Toast 提示类\n * Created by wangwentao on 2017/1/25.\n * Toast统一管理类\n */\n\npublic class ToastUtil {\n    private static boolean isShow = true;//默认显示\n    private static  Toast mToast = null;//全局唯一的Toast\n\n    /*private控制不应该被实例化*/\n    private ToastUtil() {\n        throw new UnsupportedOperationException(\"不能被实例化\");\n    }\n\n    /**\n     * 全局控制是否显示Toast\n     * @param isShowToast\n     */\n    public static void controlShow(boolean isShowToast){\n        isShow = isShowToast;\n    }\n\n    /**\n     * 取消Toast显示\n     */\n    public void cancelToast() {\n        if(isShow && mToast != null){\n            mToast.cancel();\n        }\n    }\n\n    /**\n     * 短时间显示Toast\n     *\n     * @param context\n     * @param message\n     */\n    public static void showShort(Context context, CharSequence message) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT);\n            } else {\n                mToast.setText(message);\n            }\n            mToast.show();\n        }\n    }\n\n\n    /**\n     * 短时间显示Toast\n     *\n     * @param context\n     * @param resId 资源ID:getResources().getString(R.string.xxxxxx);\n     */\n    public static void showShort(Context context, int resId) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), resId, Toast.LENGTH_SHORT);\n            } else {\n                mToast.setText(resId);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 长时间显示Toast\n     *\n     * @param context\n     * @param message\n     */\n    public static void showLong(Context context, CharSequence message) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG);\n            } else {\n                mToast.setText(message);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 长时间显示Toast\n     *\n     * @param context\n     * @param resId 资源ID:getResources().getString(R.string.xxxxxx);\n     */\n    public static void showLong(Context context, int resId) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), resId, Toast.LENGTH_LONG);\n            } else {\n                mToast.setText(resId);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义显示Toast时间\n     *\n     * @param context\n     * @param message\n     * @param duration 单位:毫秒\n     */\n    public static void show(Context context, CharSequence message, int duration) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, duration);\n            } else {\n                mToast.setText(message);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义显示Toast时间\n     *\n     * @param context\n     * @param resId 资源ID:getResources().getString(R.string.xxxxxx);\n     * @param duration 单位:毫秒\n     */\n    public static void show(Context context, int resId, int duration) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), resId, duration);\n            } else {\n                mToast.setText(resId);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义Toast的View\n     * @param context\n     * @param message\n     * @param duration 单位:毫秒\n     * @param view 显示自己的View\n     */\n    public static void customToastView(Context context, CharSequence message, int duration,View view) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, duration);\n            } else {\n                mToast.setText(message);\n            }\n            if(view != null){\n                mToast.setView(view);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义Toast的位置\n     * @param context\n     * @param message\n     * @param duration 单位:毫秒\n     * @param gravity\n     * @param xOffset\n     * @param yOffset\n     */\n    public static void customToastGravity(Context context, CharSequence message, int duration,int gravity, int xOffset, int yOffset) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, duration);\n            } else {\n                mToast.setText(message);\n            }\n            mToast.setGravity(gravity, xOffset, yOffset);\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义带图片和文字的Toast，最终的效果就是上面是图片，下面是文字\n     * @param context\n     * @param message\n     * @param iconResId 图片的资源id,如:R.drawable.icon\n     * @param duration\n     * @param gravity\n     * @param xOffset\n     * @param yOffset\n     */\n    public static void showToastWithImageAndText(Context context, CharSequence message, int iconResId,int duration,int gravity, int xOffset, int yOffset) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, duration);\n            } else {\n                mToast.setText(message);\n            }\n            mToast.setGravity(gravity, xOffset, yOffset);\n            LinearLayout toastView = (LinearLayout) mToast.getView();\n            ImageView imageView = new ImageView(context.getApplicationContext());\n            imageView.setImageResource(iconResId);\n            toastView.addView(imageView, 0);\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义Toast,针对类型CharSequence\n     * @param context\n     * @param message\n     * @param duration\n     * @param view\n     * @param isGravity true,表示后面的三个布局参数生效,false,表示不生效\n     * @param gravity\n     * @param xOffset\n     * @param yOffset\n     * @param isMargin true,表示后面的两个参数生效，false,表示不生效\n     * @param horizontalMargin\n     * @param verticalMargin\n     */\n    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) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), message, duration);\n            } else {\n                mToast.setText(message);\n            }\n            if(view != null){\n                mToast.setView(view);\n            }\n            if(isMargin){\n                mToast.setMargin(horizontalMargin, verticalMargin);\n            }\n            if(isGravity){\n                mToast.setGravity(gravity, xOffset, yOffset);\n            }\n            mToast.show();\n        }\n    }\n\n    /**\n     * 自定义Toast,针对类型resId\n     * @param context\n     * @param resId\n     * @param duration\n     * @param view :应该是一个布局，布局中包含了自己设置好的内容\n     * @param isGravity true,表示后面的三个布局参数生效,false,表示不生效\n     * @param gravity\n     * @param xOffset\n     * @param yOffset\n     * @param isMargin true,表示后面的两个参数生效，false,表示不生效\n     * @param horizontalMargin\n     * @param verticalMargin\n     */\n    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) {\n        if (isShow){\n            if (mToast == null) {\n                mToast = Toast.makeText(context.getApplicationContext(), resId, duration);\n            } else {\n                mToast.setText(resId);\n            }\n            if(view != null){\n                mToast.setView(view);\n            }\n            if(isMargin){\n                mToast.setMargin(horizontalMargin, verticalMargin);\n            }\n            if(isGravity){\n                mToast.setGravity(gravity, xOffset, yOffset);\n            }\n            mToast.show();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/anim/fade_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:interpolator/accelerate_quad\"\n    android:fromAlpha=\"0.0\"\n    android:toAlpha=\"1.0\"\n    android:duration=\"200\"  />"
  },
  {
    "path": "app/src/main/res/anim/fade_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:interpolator/decelerate_quad\"\n    android:fromAlpha=\"1.0\"\n    android:toAlpha=\"0.0\"\n    android:duration=\"200\" />"
  },
  {
    "path": "app/src/main/res/drawable/btn_press.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <corners android:radius=\"2dp\" />\n    <solid android:color=\"@color/btn_press_color\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/btn_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\"  android:drawable=\"@drawable/btn_press\" />\n    <item android:state_enabled=\"false\" android:drawable=\"@drawable/btn_press\" />\n    <item android:drawable=\"@drawable/btn_unpress\" />\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_unpress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <corners android:radius=\"2dp\" />\n    <solid android:color=\"@color/btn_unpress_color\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/dialog_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n\t<corners android:radius=\"2dp\" />\n\t<solid android:color=\"@color/dialog_bg\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/sel_white_gray.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/gray_light\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/white\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path android:pathData=\"M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"85.84757\"\n                android:endY=\"92.4963\"\n                android:startX=\"42.9492\"\n                android:startY=\"49.59793\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\"\n        tools:context=\".MainActivity\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"App版本:\" />\n\n            <TextView\n                android:id=\"@+id/app_version\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" />\n\n        </LinearLayout>\n\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"蓝牙相关\"\n            android:textColor=\"@color/blue\"\n            android:textSize=\"22sp\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"蓝牙状态：\" />\n\n            <TextView\n                android:id=\"@+id/ble_status\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/black\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"蓝牙连接状态：\" />\n\n            <TextView\n                android:id=\"@+id/ble_connect_status\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"未连接\"\n                android:textColor=\"@color/black\" />\n        </LinearLayout>\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnSearchBle\"\n            android:text=\"搜索并连接蓝牙\" />\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnMacConnect\"\n            android:text=\"使用MAC地址直接连接蓝牙\" />\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnDisconnectBle\"\n            android:text=\"断开蓝牙\" />\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnSendConfirmData\"\n            android:text=\"发送测试数据\" />\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnProtocolParse\"\n            android:text=\"蓝牙数据解析测试\" />\n\n        <Button\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"btnProtocolPacket\"\n            android:text=\"蓝牙数据打包测试\" />\n\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/activity_search_device_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#0000\"\n    android:gravity=\"center\">\n\n    <androidx.cardview.widget.CardView\n        android:layout_width=\"320dp\"\n        android:layout_height=\"260dp\"\n        app:cardBackgroundColor=\"#ffffffff\"\n        app:cardElevation=\"8dp\"\n        app:cardCornerRadius=\"5dp\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"35dp\"\n                android:orientation=\"horizontal\"\n                android:padding=\"5dp\"\n                android:background=\"@color/littleblue\">\n\n                <TextView\n                    android:id=\"@+id/tvTitle\"\n                    android:layout_width=\"0dp\"\n                    android:layout_weight=\"1\"\n                    android:layout_height=\"match_parent\"\n                    android:textColor=\"@color/white\"\n                    android:gravity=\"center_vertical\"/>\n\n                <ProgressBar\n                    android:id=\"@+id/pbScanning\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" />\n            </LinearLayout>\n\n            <FrameLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\">\n\n                <TextView\n                    android:id=\"@+id/tv_no_key\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center_horizontal\"\n                    android:padding=\"10dp\"\n                    android:text=\"没有发现设备\"/>\n\n                <ListView\n                    android:id=\"@+id/lv_devices\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:dividerHeight=\"1dp\"\n                    android:layout_margin=\"5dp\"\n                    android:divider=\"@color/colorPrimary\"\n                    android:stackFromBottom=\"true\">\n                </ListView>\n            </FrameLayout>\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"35dp\"\n                android:orientation=\"horizontal\">\n                <Button\n                    android:id=\"@+id/btn_search\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_weight=\"1\"\n                    android:text=\"搜索\"\n                    android:textColor=\"@color/white\"\n                    android:background=\"@color/colorPrimary\"/>\n\n                <LinearLayout\n                    android:background=\"@color/colorPrimary\"\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"match_parent\">\n\n                    <TextView\n                        android:layout_marginTop=\"5dp\"\n                        android:layout_marginBottom=\"5dp\"\n                        android:layout_width=\"1dp\"\n                        android:layout_height=\"match_parent\"\n                        android:background=\"@color/white\"/>\n                </LinearLayout>\n\n                <Button\n                    android:id=\"@+id/btn_history\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_weight=\"1\"\n                    android:text=\"连接历史\"\n                    android:textColor=\"@color/white\"\n                    android:background=\"@color/colorPrimary\"\n                    />\n                <LinearLayout\n                    android:background=\"@color/colorPrimary\"\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"match_parent\">\n\n                    <TextView\n                        android:layout_marginTop=\"5dp\"\n                        android:layout_marginBottom=\"5dp\"\n                        android:layout_width=\"1dp\"\n                        android:layout_height=\"match_parent\"\n                        android:background=\"@color/white\"/>\n                </LinearLayout>\n\n                <Button\n                    android:id=\"@+id/btn_cancel\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_weight=\"1\"\n                    android:text=\"取消搜索\"\n                    android:textColor=\"@color/white\"\n                    android:background=\"@color/colorPrimary\"/>\n            </LinearLayout>\n        </LinearLayout>\n    </androidx.cardview.widget.CardView>\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n/*\n** Copyright 2010, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\nhttps://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/alert_dialog_holo.xml\n\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"40dp\"\n    android:paddingRight=\"40dp\">\n\n    <LinearLayout\n\n        android:id=\"@+id/parentPanel\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:background=\"@drawable/dialog_bg\"\n        android:clickable=\"false\"\n        android:orientation=\"vertical\"\n        android:visibility=\"visible\">\n\n        <LinearLayout\n            android:id=\"@+id/topPanel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"#22000000\"\n            android:orientation=\"vertical\">\n\n            <RelativeLayout\n                android:id=\"@+id/title_template\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"45dp\"\n                android:layout_marginEnd=\"3dip\"\n                android:layout_marginStart=\"3dip\">\n\n                <ImageView\n                    android:id=\"@+id/icon\"\n                    android:layout_width=\"39dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_centerVertical=\"true\"\n                    android:scaleType=\"centerInside\"\n                    android:src=\"@null\" />\n\n                <TextView\n                    android:id=\"@+id/alertTitle\"\n                    style=\"@style/DialogWindowTitle\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_centerInParent=\"true\"\n                    android:layout_marginLeft=\"8dip\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\" />\n            </RelativeLayout>\n\n            <View\n                android:id=\"@+id/titleDivider\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0.5dp\"\n                android:background=\"@color/divider_color\"\n                android:visibility=\"visible\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/contentPanel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/message\"\n                style=\"?android:attr/textAppearanceMedium\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"15dp\"\n                android:layout_marginRight=\"15dp\"\n                android:paddingBottom=\"8dip\"\n                android:paddingEnd=\"16dip\"\n                android:paddingStart=\"16dip\"\n                android:paddingTop=\"8dip\"\n                android:textColor=\"@color/msg_color\"\n                android:textIsSelectable=\"true\" />\n        </LinearLayout>\n\n        <FrameLayout\n            android:id=\"@+id/customPanel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"></FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <Button\n                android:id=\"@+id/button1\"\n                style=\"@style/dialog_btn\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"5dp\"\n                android:text=\"OK\"\n                android:visibility=\"gone\" />\n\n            <Button\n                android:id=\"@+id/button2\"\n                style=\"@style/dialog_btn\"\n                android:layout_marginLeft=\"5dp\"\n                android:layout_marginRight=\"10dp\"\n                android:text=\"Cancle\"\n                android:visibility=\"gone\" />\n        </LinearLayout>\n    </LinearLayout>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_bluetooth_device_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"horizontal\"\n    android:background=\"@drawable/sel_white_gray\"\n    android:paddingLeft=\"5dp\"\n    android:paddingRight=\"5dp\">\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\">\n        <TextView\n            android:id=\"@+id/tv_bluetooth_name\"\n            android:layout_width=\"match_parent\"\n            android:textSize=\"17sp\"\n            android:textColor=\"@color/colorPrimary\"\n            android:layout_height=\"wrap_content\" />\n        <TextView\n            android:id=\"@+id/tv_bluetooth_address\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingLeft=\"12dp\"\n            android:textSize=\"12sp\"/>\n\n    </LinearLayout>\n\n    <com.luo.bluetooth.customview.searchble.SignalView\n        android:id=\"@+id/svBluetooth\"\n        android:layout_width=\"35dp\"\n        android:layout_height=\"35dp\"\n        android:layout_gravity=\"center\"/>\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"SignalView\">\n        <attr name=\"noIntensityColor\" format=\"color\"/>\n        <attr name=\"intensityColor\" format=\"color\"/>\n    </declare-styleable>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!--主色调-->\n    <color name=\"colorPrimaryLight\">#B3E5FC</color>\n    <color name=\"colorPrimaryDark\">#13619f</color>\n    <color name=\"colorPrimary\">#1c82d4</color>\n    <color name=\"colorAccent\">#FF4081</color>\n    <color name=\"primary_text\">#212121</color>\n    <color name=\"secondary_text\">#757575</color>\n    <color name=\"icons\">#FFFFFF</color>\n    <color name=\"iconsDisable\">#757575</color>\n    <color name=\"titleTextColor\">#FFFFFF</color>\n\n    <!--支付宝-->\n    <!--<color name=\"Indigo_colorPrimaryDark\">#13619f</color>-->\n    <!--<color name=\"Indigo_colorPrimary\">#1c82d4</color>-->\n    <!--<color name=\"Indigo_nav_color\">#4675FF</color>-->\n\n    <!--<color name=\"littleblue\">#00bbff</color>-->\n    <color name=\"littleblue\">@color/colorPrimary</color>\n\n    <color name=\"dialog_bg\">#ffe74c3c</color>\n    <color name=\"text_color\">#FFFFFF</color>\n    <color name=\"divider_color\">#11000000</color>\n    <color name=\"msg_color\">#FFFFFFFF</color>\n\n    <color name=\"btn_press_color\">#66000000</color>\n    <color name=\"btn_unpress_color\">#22000000</color>\n\n    <color name=\"gray_light\">#EAEAEA</color>\n\n    <!-- ticket color -->\n    <color name=\"ticket_green\">#00a910</color>\n\n\n    <color name=\"gray\">#E3E8ED</color>\n    <color name=\"black\">#000000</color><!--黑色 -->\n    <color name=\"white\">#FFFFFF</color><!--白色-->\n    <color name=\"ghostwhite\">#f8f8ff</color><!--幽灵白 -->\n    <color name=\"grey\">#808080</color><!--灰色 -->\n    <color name=\"gainsboro\">#dcdcdc</color><!--淡灰色 -->\n\n    <color name=\"red\">#ff0000</color><!--红色 -->\n    <color name=\"orange\">#ffa500</color><!--橙色 -->\n    <color name=\"green\">#008000</color><!--绿色 -->\n    <color name=\"blue\">#0000ff</color><!--蓝色 -->\n\n    <color name=\"float_transparent\">#00000000</color>\n    <color name=\"sweet_dialog_bg_color\">#FFFFFF</color>\n    <color name=\"button_text_color\">#FFFFFF</color>\n    <color name=\"gray_btn_bg_color\">#D0D0D0</color>\n    <color name=\"gray_btn_bg_pressed_color\">#B6B6B6</color>\n    <color name=\"blue_btn_bg_color\">#AEDEF4</color>\n    <color name=\"blue_btn_bg_pressed_color\">#96BFD2</color>\n    <color name=\"red_btn_bg_color\">#DD6B55</color>\n    <color name=\"red_btn_bg_pressed_color\">#CD5B55</color>\n    <color name=\"error_stroke_color\">#F27474</color>\n    <color name=\"success_stroke_color\">#A5DC86</color>\n    <color name=\"trans_success_stroke_color\">#33A5DC86</color>\n    <color name=\"warning_stroke_color\">#F8BB86</color>\n    <color name=\"dark_gray\">#707f84</color>\n\n    <!--seerbar-->\n    <color name=\"text_gray\">#323232</color>\n    <!-- grass -->\n    <color name=\"grass_light\">#a8e3ce</color>\n\n    <!-- dark -->\n\n    <!-- snow -->\n    <color name=\"snow_dark\">#dddddd</color>\n\n    <!-- sea -->\n\n    <!-- blood -->\n    <color name=\"blood_primary\">#d94130</color>\n\n    <!--Material Colors-->\n\n    <!--按钮背景颜色-->\n    <color name=\"btn_bg_blue\">#1fbaf3</color>\n    <color name=\"text_color_default\">#bfbfbf</color>\n    <!--主背景颜色-->\n    <color name=\"litlt_grey\">#F0F0F0</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">蓝牙示例程序</string>\n\n    <!--蓝牙开锁界面提示信息-->\n    <string name=\"title_device_list_search_key\">正在搜索设备...</string>\n    <string name=\"title_device_list_stop_search\">停止搜索设备</string>\n    <string name=\"title_device_list_history\">请选择已连接过的设备</string>\n    <string name=\"dialog_set_note_name\">设置备注名称</string>\n    <string name=\"title_device_list_select_key\">请选择要连接的设备</string>\n\n    <string name=\"dialog_ok\">OK</string>\n    <string name=\"dialog_cancel\">Cancel</string>\n    <string name=\"btn_ok\">确定</string>\n    <string name=\"btn_cancel\">取消</string>\n    <string name=\"alarm_id\">告警ID：</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"DialogTheme.TransparentNoTitle\" parent=\"Theme.AppCompat.Dialog\">\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n    </style>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n    <!--dialog-->\n    <style name=\"dialog_tran\" parent=\"android:style/Theme.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"android:backgroundDimAmount\">0.4</item>\n    </style>\n\n    <style name=\"dialog_untran\" parent=\"dialog_tran\">\n        <item name=\"android:backgroundDimEnabled\">true</item>\n    </style>\n    <style name=\"DialogWindowTitle\">\n        <item name=\"android:textSize\">22sp</item>\n        <item name=\"android:textColor\">@color/text_color</item>\n    </style>\n\n    <style name=\"dialog_btn\">\n        <item name=\"android:layout_width\">0dp</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:minHeight\">36dp</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_marginBottom\">10dp</item>\n        <item name=\"android:background\">@drawable/btn_selector</item>\n        <item name=\"android:textColor\">#ffffff</item>\n        <item name=\"android:gravity\">center</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/luo/bluetooth/ExampleUnitTest.java",
    "content": "package com.luo.bluetooth;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "bluetooth/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "bluetooth/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n    buildToolsVersion '27.0.3'\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n//    compile 'com.android.support:appcompat-v7:25.1.0'\n    testImplementation files('libs/samsung_ble_sdk_200.jar')\n    'junit:junit:4.12'\n    implementation files('libs/com.broadcom.bt.jar')\n    implementation files('libs/samsung_ble_sdk_200.jar')\n}\n"
  },
  {
    "path": "bluetooth/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in D:\\Android\\sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "bluetooth/src/androidTest/java/com/stag/bluetooth/ExampleInstrumentedTest.java",
    "content": "package com.stag.bluetooth;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.stag.bluetooth.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.stag.bluetooth\">\n\n    <uses-permission-sdk-23 android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />\n\n    <application>\n        <service\n            android:name=\".extend.BleService\"\n            android:enabled=\"true\"/>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/BluetoothController.java",
    "content": "package com.stag.bluetooth;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.text.TextUtils;\n\nimport com.stag.bluetooth.extend.BleService;\nimport com.stag.bluetooth.helper.BleHelper;\nimport com.stag.bluetooth.helper.BluetoothHelper;\nimport com.stag.bluetooth.helper.TraditionHelper;\nimport com.stag.bluetooth.helper.TraditionServerHelper;\nimport com.stag.bluetooth.protocol.Protocol;\nimport com.stag.bluetooth.util.Logs;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * 蓝牙总控制\n * Created by LPQ on 2017/11/14.\n */\n\npublic final class BluetoothController {\n\n    //Bluetooth type\n    public static final short TYPE_BLE = 1; // 客户端：低功耗蓝牙\n    public static final short TYPE_TRADITION = 2; // 客户端：传统蓝牙\n    public static final short TYPE_TRADITION_SERVER = 3; // 服务器：传统蓝牙\n    //Bluetooth scan filter\n//    public static final short SCAN_FILTER_ALL = 0;\n//    public static final short SCAN_FILTER_NAME_NON_NULL = 1;\n//    public static final short SCAN_FILTER_TYPE_MATCHING = 2;\n    private static BluetoothController controller;\n    private BluetoothHelper mHelper;\n    private Protocol mProtocol;\n    private short mBluetoothType;\n    private Context mContext;\n    private OnBluetoothScanListener mScanListener;                                 //蓝牙扫描监听\n    private OnBluetoothConnectStateChangeListener mCurrentConnectlistener; //当前controller连接状态监听\n    private OnBluetoothConnectStateChangeListener mParamConnectlistener;   //传进来的连接状态监听\n    private OnBluetoothStateChangeListener mBluetoothStateListener;     //监听手机蓝牙状态\n    private List<BluetoothDevice> mDevices;\n    private boolean mScanRemoveRepeat;          //搜索设备进行排重检查\n    private boolean isScanning;\n    private boolean isStartScanning;\n\n    public static BluetoothController getController(Context context) {\n        if (controller == null) {\n            synchronized (BluetoothController.class) {\n                if (controller == null)\n                    if (context == null)\n                        throw new NullPointerException(\"BluetoothController init fail, context is null!\");\n                controller = new BluetoothController(context);\n            }\n        }\n        return controller;\n    }\n\n    private BluetoothController(Context context) {\n        mContext = context.getApplicationContext();\n        mDevices = new LinkedList<BluetoothDevice>();\n        mCurrentConnectlistener = new OnBluetoothConnectStateChangeListener() {\n            @Override\n            public void onBluetoothConnect(BluetoothDevice device, boolean isSuccess) {\n                if (isSuccess) {\n                /*设置发送方法*/\n                    setSendMethod();\n                    /**启动蓝牙传输*/\n                    BluetoothTransfer.getInstance().start();\n                }\n                if (mParamConnectlistener != null)\n                    mParamConnectlistener.onBluetoothConnect(device, isSuccess);\n            }\n\n            @Override\n            public void onBluetoothDisconnect(BluetoothDevice device) {\n                BluetoothTransfer.getInstance().stop();\n                setProtocol(null);\n                if (mParamConnectlistener != null)\n                    mParamConnectlistener.onBluetoothDisconnect(device);\n            }\n        };\n        setBluetoothType(TYPE_BLE);\n    }\n\n    public OnBluetoothConnectStateChangeListener getConnectStateChangeListener() {\n        return mParamConnectlistener;\n    }\n\n    public void registerConnectStateChangeListener(final OnBluetoothConnectStateChangeListener listener) {\n        mParamConnectlistener = listener;\n    }\n\n    public void unregisterConnectStateChangeListener() {\n        registerConnectStateChangeListener(null);\n    }\n\n    public OnBluetoothStateChangeListener getBluetoothStateChangeListener() {\n        return mBluetoothStateListener;\n    }\n\n    public void registerBluetoothStateChangeListener(OnBluetoothStateChangeListener listener) {\n        mBluetoothStateListener = listener;\n    }\n\n    public void unregisterBluetoothStateChangeListener(){\n        registerBluetoothStateChangeListener(null);\n    }\n\n    public OnBluetoothTransmitListener getTransmitListener() {\n        if (mHelper != null)\n            return mHelper.getTransmitListener();\n        return null;\n    }\n\n    public void registerTransmitListener(OnBluetoothTransmitListener transmitListener) {\n//        BleHelper.getInstance(mContext).setTransmitListener(transmitListener);\n//        TraditionHelper.getInstance(mContext).setTransmitListener(transmitListener);\n        mHelper.setTransmitListener(transmitListener);\n    }\n\n    public void unregisterTransmitListener(){\n        registerTransmitListener(null);\n    }\n\n    public void setBluetoothType(short bluetoothType) {\n        if (mBluetoothType == bluetoothType)\n            return;\n        mBluetoothType = bluetoothType;\n        if (mBluetoothType == TYPE_BLE) {\n            mHelper = BleHelper.getInstance(mContext);\n        } else if (mBluetoothType == TYPE_TRADITION){\n            mHelper = TraditionHelper.getInstance(mContext);\n        } else {\n            mHelper = TraditionServerHelper.getInstance(mContext);\n        }\n        mHelper.setProtocol(mProtocol);\n        mHelper.setConnectStateChangeListener(mCurrentConnectlistener);\n        setSendMethod();\n    }\n\n    public void setProtocol(Protocol protocol) {\n        if (mProtocol != null) {\n            mProtocol.destroy();\n        }\n        mProtocol = protocol;\n        if (mProtocol!=null)\n            mProtocol.initialize();\n        if (mHelper != null)\n            mHelper.setProtocol(protocol);\n        BluetoothTransfer.getInstance().setProtocol(mProtocol);\n    }\n\n    public Protocol getProtocol() {\n        return mProtocol;\n    }\n\n    /**\n     * 是否已经连接设备\n     */\n    public boolean isConnected() {\n        if (mHelper != null)\n            return mHelper.isConnected();\n        return false;\n    }\n\n    public boolean isConnecting(){\n        if (mHelper != null)\n            return mHelper.isConnecting();\n        return false;\n    }\n\n    public boolean isScanning(){\n        return isScanning;\n    }\n\n    /**\n     * 获取蓝牙类型,BLE或者传统\n     */\n    public short getBluetoothType() {\n        return mBluetoothType;\n    }\n\n    public boolean setBleHighConnectionPriority(boolean flag){\n        boolean res = false;\n        if (mHelper!=null && mBluetoothType==TYPE_BLE)\n            res = ((BleHelper) mHelper).setHighConnectionPriority(flag);\n        return res;\n    }\n\n    public boolean setBleHighSpeedMode(boolean flag){\n        boolean res = false;\n        if (mHelper!=null && mBluetoothType==TYPE_BLE)\n            res = ((BleHelper) mHelper).setHighSpeedMode(flag);\n        return res;\n    }\n\n    public boolean isBleHighSpeedMode(){\n        boolean res = false;\n        if (mHelper!=null && mBluetoothType==TYPE_BLE)\n            res = ((BleHelper) mHelper).isHighSpeedMode();\n        return res;\n    }\n\n    /**\n     * 设置发送方法\n     */\n    private void setSendMethod() {\n        BluetoothTransfer.getInstance().setSend(new BluetoothTransfer.SendMethod() {\n            @Override\n            public void send(byte[] data) {\n                mHelper.send(data);\n            }\n        });\n    }\n\n    public void sendData(byte[] data) {\n        if (mHelper != null) {\n            mHelper.send(data);\n        }\n    }\n\n    /**\n     * 连接设备，必须先设置Protocol或者使用connect(String address, Protocol protocol)\n     */\n    public void connect(String address) {\n        connect(address, mProtocol);\n    }\n\n    public void connect(String address, Protocol protocol){\n        setProtocol(protocol);\n        mHelper.connect(address);\n    }\n\n    /**\n     * 断开设备\n     */\n    public void disconnect() {\n        mHelper.disconnect();\n        unregisterBluetoothBroadcast();\n        BluetoothTransfer.getInstance().stop();\n    }\n\n    /**\n     * 扫描设备\n     */\n    public void startScan(OnBluetoothScanListener listener) {\n        if (isScanning) {\n            isStartScanning = true;\n            stopScan();\n        }\n        if (mScanListener!=listener)\n            mScanListener = listener;\n        registerBluetoothBroadcast();\n        isScanning = true;\n        mHelper.startScan();\n//        BleHelper.getInstance(mContext).startScan();\n//        TraditionHelper.getInstance(mContext).startScan();\n    }\n\n    /**\n     * 停止扫描\n     */\n    public void stopScan() {\n        if (isScanning) {\n            mScanListener = null;//防止DeviceListActivity内存泄漏\n            mHelper.stopScan();\n//            BleHelper.getInstance(mContext).stopScan();\n//            TraditionHelper.getInstance(mContext).stopScan();\n            if (isScanRemoveRepeat())\n                mDevices.clear();\n            isScanning = false;\n        }\n    }\n\n    /*private void stopScan(boolean isSelf){\n        if (isSelf){\n            stopScan();\n        }\n    }*/\n\n    private void registerBluetoothBroadcast() {\n        IntentFilter filter = new IntentFilter();\n        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);\n        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);\n        filter.addAction(BluetoothDevice.ACTION_FOUND);\n        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);\n        filter.addAction(BleService.BLE_DEVICE_FOUND);\n        filter.addAction(BleHelper.BLE_SCAN_FINISH);\n        mContext.registerReceiver(mReceiver, filter);\n    }\n\n    private void unregisterBluetoothBroadcast() {\n        if (mReceiver != null) {\n            try {\n                mContext.unregisterReceiver(mReceiver);\n            } catch (Exception e) {\n\n            }\n        }\n    }\n\n    private BroadcastReceiver mReceiver = new BroadcastReceiver() {\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            BluetoothDevice device = null;\n            int rssi;\n            switch (intent.getAction()){\n                case BluetoothAdapter.ACTION_STATE_CHANGED:\n                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);\n                    switch (blueState) {\n//                    case BluetoothAdapter.STATE_TURNING_ON:\n                        case BluetoothAdapter.STATE_ON:\n                            if (mBluetoothStateListener != null)\n                                mBluetoothStateListener.onBluetoothOpen();\n                            break;\n//                    case BluetoothAdapter.STATE_TURNING_OFF:\n                        case BluetoothAdapter.STATE_OFF:\n                            if (mBluetoothStateListener != null)\n                                mBluetoothStateListener.onBluetoothClose();\n                            break;\n                        default:\n                            break;\n                    }\n                    break;\n                case BluetoothDevice.ACTION_ACL_DISCONNECTED:\n                    Logs.d(\"LPQ\", \"discontect from broadcast\");\n                    if (mHelper != null)\n                        mHelper.disconnect();\n                    break;\n                case BluetoothDevice.ACTION_FOUND:\n                    device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);\n                    rssi = (int) intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, (short) 0);\n                    handleScanFindResult(device, rssi, false);\n                    break;\n                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:\n                    if (mScanListener != null)\n                        mScanListener.onBluetoothScanFinish();\n                    if (isStartScanning){\n                        isStartScanning = false;\n                    }else {\n                        stopScan();\n                    }\n                    break;\n                case BleService.BLE_DEVICE_FOUND:\n                    device = intent.getParcelableExtra(BleService.EXTRA_DEVICE);\n                    rssi = intent.getIntExtra(BleService.EXTRA_RSSI, 0);\n                    handleScanFindResult(device, rssi, true);\n                    break;\n                case BleHelper.BLE_SCAN_FINISH:\n                    //因为BLE的扩展SDK没有这个，这个是另外加的，由BleHelper发送\n                    if (mScanListener != null)\n                        mScanListener.onBluetoothScanFinish();\n                    break;\n                default:break;\n            }\n\n        }\n    };\n\n    /**\n     * 处理扫描发现结果\n     */\n    private void handleScanFindResult(BluetoothDevice device, int rssi, boolean isBle) {\n        if (device == null) {\n            return;\n        }\n        if (TextUtils.isEmpty(device.getName())) {\n            return;\n        }\n\n        if (mScanRemoveRepeat) {\n            for (BluetoothDevice m : mDevices) {\n                if (m.getAddress().equals(device.getAddress())) {\n                    return;\n                }\n            }\n            mDevices.add(device);\n        }\n\n        if (mScanListener != null) {\n            mScanListener.onBluetoothScanFindDevice(device, rssi, isBle);\n        }\n    }\n\n    public void setScanRemoveRepeat(boolean b) {\n        mScanRemoveRepeat = b;\n    }\n\n    public boolean isScanRemoveRepeat() {\n        return mScanRemoveRepeat;\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/BluetoothDispatch.java",
    "content": "package com.stag.bluetooth;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * 派遣所有回调的事件，根据Task分发到主线程或者子线程，建立线程池分发执行\n * Created by LPQ on 2016/11/18.\n */\n\npublic final class BluetoothDispatch {\n\n    private static BluetoothDispatch instance;\n    private ExecutorService mCatchThreadPool;\n    private Handler mMainHandler;\n\n    public static BluetoothDispatch getInstance(){\n        if (instance==null){\n            synchronized (BluetoothDispatch.class){\n                if (instance==null)\n                    instance = new BluetoothDispatch();\n            }\n        }\n        return instance;\n    }\n\n    public void start(){\n        if (mCatchThreadPool ==null){\n            mCatchThreadPool = Executors.newCachedThreadPool();\n        }\n        mMainHandler = new Handler(Looper.getMainLooper());\n    }\n\n    public void stop(){\n        if (mCatchThreadPool !=null){\n            mCatchThreadPool.shutdownNow();\n            mCatchThreadPool = null;\n        }\n        mMainHandler = null;\n    }\n\n    public void dispatch(final Callback callback, boolean runInMainThread){\n        if (callback==null) return;\n\n        if (runInMainThread){\n            mMainHandler.post(new Runnable() {\n                @Override\n                public void run() {\n                    callback.callback();\n                }\n            });\n        }else {\n            if (mCatchThreadPool !=null){\n                mCatchThreadPool.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        callback.callback();\n                    }\n                });\n            }\n        }\n    }\n\n    /**\n     * 以防扩展\n     * */\n    public interface Callback{\n        void callback();\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/BluetoothTask.java",
    "content": "package com.stag.bluetooth;\n\nimport com.stag.bluetooth.packet.Packet;\n\n/**\n * 蓝牙发送任务\n * Created by Bruce on 2016/11/15.\n */\n\npublic final class BluetoothTask<T extends Packet> {\n\n    private static int DEFAULT_TRY_COUNT = 2;\n    private static int DEFAULT_TIMEOUT = 3000;      //默认超时时间，毫秒级\n    private static boolean DEFAULT_MAIN_THREAD = false;\n\n    private int tryCount;                           //尝试发送次数\n    private long timeout;                            //每次重发的超时时间，毫秒级\n    private long sentTime;                          //至少在应用层真正被发送出去的时间\n    private T packet;                          //数据包\n    //    private int priority;                           //优先级，暂时业务不需要\n    private boolean resultCallbackInMainThread;  //结果回调是否需要执行在主线程上\n    private OnResultListener onResult;              //响应回调\n\n    public <E extends T> BluetoothTask(E packet) {\n        this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, DEFAULT_MAIN_THREAD, null);\n    }\n\n    public <E extends T> BluetoothTask(E packet, OnResultListener<E> onResult) {\n        this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, DEFAULT_MAIN_THREAD, onResult);\n    }\n\n    public <E extends T>BluetoothTask(E packet, boolean resultCallbackInMainThread, OnResultListener<E> onResult) {\n        this(DEFAULT_TRY_COUNT, DEFAULT_TIMEOUT, packet, resultCallbackInMainThread, onResult);\n    }\n\n    public <E extends T> BluetoothTask(int tryCount, int timeout, E packet, OnResultListener<E> onResult) {\n        this(tryCount, timeout, packet, DEFAULT_MAIN_THREAD, onResult);\n    }\n\n    public <E extends T> BluetoothTask(int tryCount, int timeout, E packet, boolean resultCallbackInMainThread, OnResultListener<E> onResult) {\n        this.tryCount = tryCount;\n        setTimeout(timeout);\n        this.packet = packet;\n        this.resultCallbackInMainThread = resultCallbackInMainThread;\n        this.onResult = onResult;\n    }\n\n    public boolean isTimeout() {\n        long t = (System.nanoTime() - sentTime);\n        return t > timeout && sentTime != 0;\n    }\n\n    /**\n     * 异步发送\n     * @return\n     */\n    public void send() {\n        BluetoothTransfer.getInstance().addSendTask(this);\n    }\n\n    /**\n     * 阻塞发送，返回数据部分的字节数组\n     * @return\n     */\n    public byte[] sendBySync2() {\n        if (BluetoothTransfer.getInstance().isStop())\n            return null;\n        final byte[][] res = new byte[1][];\n        synchronized (res) {\n            setOnResult(new OnDataResultListener() {\n                @Override\n                public void onResult(boolean isTimeout, byte[] data) {\n                    synchronized (res) {\n                        res[0] = data;\n                        res.notify();\n                    }\n                }\n            });\n            send();\n            try {\n                res.wait();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n        return res[0];\n    }\n\n    /**\n     * 阻塞发送，返回完整的包\n     * @return\n     */\n    public T sendBySync() {\n        if (BluetoothTransfer.getInstance().isStop())\n            return null;\n        final T[] res = (T[]) new Packet[1];\n        synchronized (this) {\n            setOnResult(new OnResultListener<T>() {\n                @Override\n                public void onResult(boolean isTimeout, T packet) {\n                    synchronized (res) {\n                        res[0] = packet;\n                        res.notify();\n                    }\n                }\n            });\n            send();\n            try {\n                res.wait();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n        return res[0];\n    }\n\n    public int getTryCount() {\n        return tryCount;\n    }\n\n    public BluetoothTask setTryCount(int tryCount) {\n        this.tryCount = tryCount;\n        return this;\n    }\n\n    public long getTimeout() {\n        return timeout / 1000000;\n    }\n\n    public BluetoothTask setTimeout(long timeout) {\n        this.timeout = timeout * 1000000;\n        return this;\n    }\n\n    public long getSentTime() {\n        return sentTime;\n    }\n\n    BluetoothTask setSentTime(long sendTime) {\n        this.sentTime = sendTime;\n        return this;\n    }\n\n    public Packet getPacket() {\n        return packet;\n    }\n\n    public BluetoothTask setPacket(T packet) {\n        this.packet = packet;\n        return this;\n    }\n\n    public boolean isResultCallbackInMainThread() {\n        return resultCallbackInMainThread;\n    }\n\n    public BluetoothTask setResultCallbackInMainThread(boolean resultCallbackInMainThread) {\n        this.resultCallbackInMainThread = resultCallbackInMainThread;\n        return this;\n    }\n\n    public OnResultListener getOnResult() {\n        return onResult;\n    }\n\n    public BluetoothTask setOnResult(OnResultListener onResult) {\n        this.onResult = onResult;\n        return this;\n    }\n\n    /**\n     * 只获取数据部分\n     */\n    public abstract static class OnDataResultListener implements OnResultListener<Packet> {\n\n        @Override\n        public void onResult(boolean isTimeout, Packet packet) {\n            onResult(isTimeout, packet == null ? null : packet.getData());\n        }\n        \n        public abstract void onResult(boolean isTimeout, byte[] data);\n    }\n\n    /**\n     * 返回整个回应包\n     */\n    public interface OnResultListener<T extends Packet> {\n        void onResult(boolean isTimeout, T packet);\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/BluetoothTransfer.java",
    "content": "package com.stag.bluetooth;\n\nimport com.stag.bluetooth.protocol.ParseResult;\nimport com.stag.bluetooth.protocol.Protocol;\nimport com.stag.bluetooth.util.ByteUtils;\n\nimport java.nio.ByteBuffer;\nimport java.util.Iterator;\nimport java.util.LinkedList;\n\n/**\n * 蓝牙传输控制类\n * 目前不支持流水号，如果同一条指令并发发送，采取FIFO的形式\n * Created by LPQ on 2016/11/14.\n */\n\npublic final class BluetoothTransfer {\n\n    private static final short RECV_BUFFER_SIZE = 1024;       //接收缓冲区大小\n    private static final short TIMEOUT_CHECK_ACCURACY = 100; //超时检查的精确度，即超时时间的误差范围，毫秒级\n    public static BluetoothTransfer instance;\n    private Protocol mProtocol;                             //所使用协议\n    private SendMethod send;                                //发送蓝牙数据的方法，发送时必须设置好Task的发送时间\n    private LinkedList<BluetoothTask> sendList;             //发送任务执行队列，暂时先自行管理同步\n    private LinkedList<BluetoothTask> waitRespondList;      //待响应队列，暂时先自行管理同步\n    private ByteBuffer recvBuf;                               //接收缓冲区\n    private Thread sendThread;                              //负责发送\n    private Thread recvHandlerThread;                       //负责处理接收到的数据\n    private Thread timeoutCheckThread;\n    private boolean isResume;\n    private Object recvLock, sendLock, waitRespondLock;\n    private BluetoothDispatch dispatch;\n\n    public static BluetoothTransfer getInstance(){\n        if (instance==null){\n            synchronized (BluetoothTransfer.class){\n                if (instance==null)\n                    instance = new BluetoothTransfer();\n            }\n        }\n        return instance;\n    }\n\n    private BluetoothTransfer(){\n        sendList = new LinkedList<>();\n        waitRespondList = new LinkedList<>();\n        recvBuf = ByteBuffer.allocate(RECV_BUFFER_SIZE);\n        recvLock = new Object();\n        sendLock = new Object();\n        waitRespondLock = new Object();\n        dispatch = BluetoothDispatch.getInstance();\n    }\n\n    /**\n     * 启动蓝牙传输\n     * */\n    public void start(){\n        stop();\n        dispatch.start();\n        isResume = true;\n        startSendQueue();\n        startTimeoutCheck();\n        startRecvHandlerQueue();\n    }\n\n    /**\n     * 停止蓝牙传输\n     * */\n    public void stop(){\n        dispatch.stop();\n        isResume = false;\n\n        stopSendQueue();\n        stopRecvHandlerQueue();\n        stopTimeoutCheck();\n\n        synchronized (sendLock){\n            for (BluetoothTask task:sendList){\n                if (task.getOnResult()!=null)\n                    task.getOnResult().onResult(true, null);\n            }\n            sendList.clear();\n        }\n        synchronized (waitRespondLock){\n            for (BluetoothTask task:waitRespondList){\n                if (task.getOnResult()!=null)\n                    task.getOnResult().onResult(true, null);\n            }\n            waitRespondList.clear();\n        }\n        synchronized (recvLock){\n            recvBuf.clear();\n        }\n    }\n\n    /**\n     * 蓝牙发送数据\n     * */\n    private void startSendQueue(){\n        sendThread = new Thread(){\n            @Override\n            public void run() {\n                super.run();\n                while (isResume){\n                    BluetoothTask task;\n                    synchronized (sendLock){\n                        while (sendList.isEmpty()){\n                            try {\n                                sendLock.wait();\n                            } catch (InterruptedException e) {\n                                e.printStackTrace();\n                            }\n                            if (!isResume)\n                                return;\n                            /*检查和处理超时的已发送的待响应任务*/\n//                            checkTimeoutTask();\n                        }\n                        /*发送数据，把task放入待响应队列*/\n                        task = sendList.removeFirst();\n                    }\n                    if (task.getOnResult()!=null){\n                        synchronized (waitRespondLock){\n                            waitRespondList.add(task);\n                        }\n                    }\n                    send.send(mProtocol.packetToBytes(task.getPacket()));\n                    task.setSentTime(System.nanoTime());\n                }\n            }\n        };\n        sendThread.start();\n    }\n\n    /**\n     * 蓝牙收到数据后的处理\n     * */\n    private void startRecvHandlerQueue(){\n        recvHandlerThread = new Thread(){\n            @Override\n            public void run() {\n                super.run();\n                while (isResume){\n                    synchronized (recvLock){\n                        while (recvBuf.position()==0){\n                            try {\n                                recvLock.wait();\n                            } catch (InterruptedException e) {\n                                e.printStackTrace();\n                            }\n                            if (!isResume)\n                                return;\n                        }\n                        final ParseResult result = mProtocol.parse(ByteUtils.getBytesWithBuffer(recvBuf));\n                        recvBuf.clear();\n                        switch (result.getType()){\n                            case RESPOND://响应事件\n                                synchronized (waitRespondLock){\n                                    Iterator<BluetoothTask> iterator = waitRespondList.iterator();\n                                    while (iterator.hasNext()){\n                                        final BluetoothTask task = iterator.next();\n                                        if (task.getPacket().match(result.getPacket())){\n                                            dispatch.dispatch(new BluetoothDispatch.Callback() {\n                                                @Override\n                                                public void callback() {\n                                                    if (task.getOnResult()!=null)\n                                                        task.getOnResult().onResult(false, result.getPacket());\n                                                }\n                                            }, task.isResultCallbackInMainThread());\n                                            iterator.remove();\n                                            break;\n                                        }\n                                    }\n                                }\n                                break;\n                            case ACTIVE://主动事件\n                                dispatch.dispatch(result.getCallback(), false);\n                                break;\n                            case INCOMPLETE://非完整一帧\n                                //好像暂时没啥事干，多余的数据在对应协议里会保存以备下次解析\n                                break;\n                            default:break;\n                        }\n                    }\n                }\n            }\n        };\n        recvHandlerThread.start();\n    }\n\n    private void startTimeoutCheck(){\n        timeoutCheckThread = new Thread(){\n            @Override\n            public void run() {\n                super.run();\n                while (isResume){\n                    try {\n                        Thread.sleep(TIMEOUT_CHECK_ACCURACY);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    if (!isResume)\n                        return;\n\n                    synchronized (waitRespondLock){\n                        Iterator<BluetoothTask> iterator = waitRespondList.iterator();\n                        while (iterator.hasNext()){\n                            final BluetoothTask task = iterator.next();\n                            if (task.isTimeout()){\n                                if (task.getTryCount()>1){\n                                    task.setTryCount(task.getTryCount()-1);\n                                    addSendTask(task);\n                                }else {\n                                    BluetoothDispatch.getInstance().dispatch(new BluetoothDispatch.Callback() {\n                                        @Override\n                                        public void callback() {\n                                            task.getOnResult().onResult(true, null);\n                                        }\n                                    }, task.isResultCallbackInMainThread());\n                                }\n                                iterator.remove();\n                            }else {\n//                                Logs.d(\"LPQ\", \"没有timeout\");\n                            }\n                        }\n                    }\n                }\n            }\n        };\n        timeoutCheckThread.start();\n    }\n\n    private void stopSendQueue(){\n        if (sendThread!=null){\n            sendThread.interrupt();\n            sendThread = null;\n        }\n    }\n\n    private void stopRecvHandlerQueue(){\n        if (recvHandlerThread !=null){\n            recvHandlerThread.interrupt();\n            recvHandlerThread = null;\n        }\n    }\n\n    private void stopTimeoutCheck(){\n        if (timeoutCheckThread !=null){\n            timeoutCheckThread.interrupt();\n            timeoutCheckThread = null;\n        }\n    }\n\n    public boolean addSendTask(BluetoothTask task){\n        if (isResume){\n            synchronized (sendLock){\n                task.setSentTime(0);\n                sendList.add(task);\n                sendLock.notify();\n            }\n        }else {\n            if (task.getOnResult()!=null)\n                task.getOnResult().onResult(true, null);\n        }\n        return isResume;\n    }\n\n    public void addRecvData(byte[] data){\n        synchronized (recvLock){\n            recvBuf.put(data);\n            recvLock.notify();\n        }\n    }\n\n    public boolean isStop(){\n        return !isResume;\n    }\n\n    /**\n     * 检查和处理超时的发送任务\n     * */\n    private void checkTimeoutTask(){\n        synchronized (waitRespondLock){\n            Iterator<BluetoothTask> iterator = waitRespondList.iterator();\n            while (iterator.hasNext()){\n                final BluetoothTask task = iterator.next();\n                if (task.isTimeout()){\n                    if (task.getTryCount()>1){\n                        task.setTryCount(task.getTryCount()-1);\n                        sendList.add(task);\n                    }else {\n                        BluetoothDispatch.getInstance().dispatch(new BluetoothDispatch.Callback() {\n                            @Override\n                            public void callback() {\n                                task.getOnResult().onResult(true, null);\n                            }\n                        }, task.isResultCallbackInMainThread());\n                    }\n                    iterator.remove();\n                }\n            }\n        }\n    }\n\n    public Protocol getProtocol() {\n        return mProtocol;\n    }\n\n    public void setProtocol(Protocol mProtocol) {\n        this.mProtocol = mProtocol;\n    }\n\n    public SendMethod getSend() {\n        return send;\n    }\n\n    public void setSend(SendMethod send) {\n        this.send = send;\n    }\n\n    public interface SendMethod{\n        void send(byte[] data);\n    }\n}"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothConnectStateChangeListener.java",
    "content": "package com.stag.bluetooth;\n\nimport android.bluetooth.BluetoothDevice;\n\n/**\n * 连接状态监听\n * Created by Administrator on 2016/11/17.\n */\n\npublic interface OnBluetoothConnectStateChangeListener {\n    /**\n     * 蓝牙设备连接回调\n     * @param device 蓝牙设备\n     * @param isSuccess 是否成功\n     * */\n    void onBluetoothConnect(BluetoothDevice device, boolean isSuccess);\n\n    /**\n     * 蓝牙设备断开回调\n     * @param device 蓝牙设备\n     * */\n    void onBluetoothDisconnect(BluetoothDevice device);\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothScanListener.java",
    "content": "package com.stag.bluetooth;\n\nimport android.bluetooth.BluetoothDevice;\n\n/**\n * 蓝牙扫描监听\n * Created by Administrator on 2016/11/25.\n */\n\npublic interface OnBluetoothScanListener {\n\n    void onBluetoothScanFindDevice(BluetoothDevice device, int rssi, boolean isBle);\n\n    void onBluetoothScanFinish();\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothStateChangeListener.java",
    "content": "package com.stag.bluetooth;\n\n/**\n * 监听手机蓝牙状态\n * Created by Administrator on 2016/11/18.\n */\n\npublic interface OnBluetoothStateChangeListener {\n    /**手机蓝牙打开*/\n    void onBluetoothOpen();\n    /**手机蓝牙关闭*/\n    void onBluetoothClose();\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/OnBluetoothTransmitListener.java",
    "content": "package com.stag.bluetooth;\n\n/**\n * 蓝牙数据收发监听\n * Created by Administrator on 2016/12/21.\n */\n\npublic interface OnBluetoothTransmitListener {\n\n    void onBluetoothSendData(byte[] data);\n    void onBluetoothRecvData(byte[] data);\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/AndroidBle.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.annotation.SuppressLint;\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.bluetooth.BluetoothGatt;\nimport android.bluetooth.BluetoothGattCallback;\nimport android.bluetooth.BluetoothGattCharacteristic;\nimport android.bluetooth.BluetoothGattDescriptor;\nimport android.bluetooth.BluetoothGattService;\nimport android.bluetooth.BluetoothManager;\nimport android.bluetooth.BluetoothProfile;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.util.Log;\n\nimport com.stag.bluetooth.extend.BleRequest.RequestType;\n\n//import org.apache.commons.codec.binary.Hex;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\n@SuppressLint(\"NewApi\")\npublic class AndroidBle implements IBle, IBleRequestHandler {\n\n\tprotected static final String TAG = \"blelib\";\n\n\tprivate BleService mService;\n\tprivate BluetoothAdapter mBtAdapter;\n\tprivate Map<String, BluetoothGatt> mBluetoothGatts;\n\t// private BTQuery btQuery;\n\n\tprivate BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {\n\t\t@Override\n\t\tpublic void onLeScan(final BluetoothDevice device, int rssi,\n\t\t\t\tbyte[] scanRecord) {\n\t\t\tmService.bleDeviceFound(device, rssi, scanRecord,\n\t\t\t\t\tBleService.DEVICE_SOURCE_SCAN);\n\t\t}\n\t};\n\n\tprivate BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {\n\t\t@Override\n\t\tpublic void onConnectionStateChange(BluetoothGatt gatt, int status,\n\t\t\t\tint newState) {\n\t\t\tString address = gatt.getDevice().getAddress();\n\t\t\tLog.d(TAG, \"onConnectionStateChange \" + address + \" status \"\n\t\t\t\t\t+ status + \" newState \" + newState);\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tdisconnect(address);\n\t\t\t\tmService.bleGattDisConnected(address);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (newState == BluetoothProfile.STATE_CONNECTED) {\n\t\t\t\tmService.bleGattConnected(gatt.getDevice());\n\t\t\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\t\t\tRequestType.DISCOVER_SERVICE, address));\n\t\t\t} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {\n\t\t\t\tmService.bleGattDisConnected(address);\n\t\t\t\tdisconnect(address);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onServicesDiscovered(BluetoothGatt gatt, int status) {\n\t\t\tString address = gatt.getDevice().getAddress();\n\t\t\tLog.d(TAG, \"onServicesDiscovered \" + address + \" status \" + status);\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.DISCOVER_SERVICE, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmService.bleServiceDiscovered(gatt.getDevice().getAddress());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicRead(BluetoothGatt gatt,\n\t\t\t\tBluetoothGattCharacteristic characteristic, int status) {\n\t\t\tString address = gatt.getDevice().getAddress();\n\t\t\tLog.d(TAG, \"onCharacteristicRead \" + address + \" status \" + status);\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.READ_CHARACTERISTIC, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Log.d(TAG, \"data \" + characteristic.getStringValue(0));\n\t\t\tmService.bleCharacteristicRead(gatt.getDevice().getAddress(),\n\t\t\t\t\tcharacteristic.getUuid().toString(), status,\n\t\t\t\t\tcharacteristic.getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicChanged(BluetoothGatt gatt,\n\t\t\t\tBluetoothGattCharacteristic characteristic) {\n\t\t\tString address = gatt.getDevice().getAddress();\n//\t\t\tLog.d(TAG, \"onCharacteristicChanged \" + address);\n//\t\t\tLog.d(TAG, new String(Hex.encodeHex(characteristic.getValue())));\n\t\t\tmService.bleCharacteristicChanged(address, characteristic.getUuid()\n\t\t\t\t\t.toString(), characteristic.getValue());\n\t\t}\n\n\t\tpublic void onCharacteristicWrite(BluetoothGatt gatt,\n\t\t\t\tBluetoothGattCharacteristic characteristic, int status) {\n\t\t\tString address = gatt.getDevice().getAddress();\n\t\t\tLog.d(TAG, \"onCharacteristicWrite \" + address + \" status \" + status);\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.WRITE_CHARACTERISTIC, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmService.bleCharacteristicWrite(gatt.getDevice().getAddress(),\n\t\t\t\t\tcharacteristic.getUuid().toString(), status);\n\t\t};\n\n\t\tpublic void onDescriptorWrite(BluetoothGatt gatt,\n\t\t\t\tBluetoothGattDescriptor descriptor, int status) {\n\t\t\tString address = gatt.getDevice().getAddress();\n\t\t\tLog.d(TAG, \"onDescriptorWrite \" + address + \" status \" + status);\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_INDICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {\n\t\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\t\tRequestType.CHARACTERISTIC_NOTIFICATION, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), true,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\t\tmService.bleCharacteristicIndication(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), status);\n\t\t\t\t} else {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), false,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t};\n\t};\n\n\tpublic AndroidBle(BleService service) {\n\t\tmService = service;\n\t\t// btQuery = BTQuery.getInstance();\n\t\tif (!mService.getPackageManager().hasSystemFeature(\n\t\t\t\tPackageManager.FEATURE_BLUETOOTH_LE)) {\n\t\t\tmService.bleNotSupported();\n\t\t\treturn;\n\t\t}\n\n\t\tfinal BluetoothManager bluetoothManager = (BluetoothManager) mService\n\t\t\t\t.getSystemService(Context.BLUETOOTH_SERVICE);\n\n\t\tmBtAdapter = bluetoothManager.getAdapter();\n\t\tif (mBtAdapter == null) {\n\t\t\tmService.bleNoBtAdapter();\n\t\t}\n\t\tmBluetoothGatts = new HashMap<String, BluetoothGatt>();\n\t}\n\n\t@Override\n\tpublic void startScan() {\n\t\tmBtAdapter.startLeScan(mLeScanCallback);\n\t}\n\n\t@Override\n\tpublic void stopScan() {\n\t\tmBtAdapter.stopLeScan(mLeScanCallback);\n\t}\n\n\t@Override\n\tpublic boolean adapterEnabled() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.isEnabled();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean connect(String address) {\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\tBluetoothGatt gatt = device.connectGatt(mService, false, mGattCallback);\n\t\tif (gatt == null) {\n\t\t\tmBluetoothGatts.remove(address);\n\t\t\treturn false;\n\t\t} else {\n\t\t\t// TODO: if state is 141, it can be connected again after about 15\n\t\t\t// seconds\n\t\t\tmBluetoothGatts.put(address, gatt);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void disconnect(String address) {\n\t\tif (mBluetoothGatts.containsKey(address)) {\n\t\t\tBluetoothGatt gatt = mBluetoothGatts.remove(address);\n\t\t\tif (gatt != null) {\n\t\t\t\tgatt.disconnect();\n\t\t\t\tgatt.close();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic ArrayList<BleGattService> getServices(String address) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tArrayList<BleGattService> list = new ArrayList<BleGattService>();\n\t\tList<BluetoothGattService> services = gatt.getServices();\n\t\tfor (BluetoothGattService s : services) {\n\t\t\tBleGattService service = new BleGattService(s);\n\t\t\t// service.setInfo(btQuery.getGattServiceInfo(s.getUuid()));\n\t\t\tlist.add(service);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic boolean requestReadCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,\n\t\t\t\tgatt.getDevice().getAddress(), characteristic));\n\t\treturn true;\n\t}\n\n\tpublic boolean readCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn gatt.readCharacteristic(characteristic.getGattCharacteristicA());\n\t}\n\n\t@Override\n\tpublic boolean discoverServices(String address) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean ret = gatt.discoverServices();\n\t\tif (!ret) {\n\t\t\tdisconnect(address);\n\t\t}\n\t\treturn ret;\n\t}\n\n\t@Override\n\tpublic BleGattService getService(String address, UUID uuid) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tBluetoothGattService service = gatt.getService(uuid);\n\t\tif (service == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn new BleGattService(service);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean requestCharacteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_NOTIFICATION, gatt.getDevice()\n\t\t\t\t\t\t.getAddress(), characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean characteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBleRequest request = mService.getCurrentRequest();\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean enable = true;\n\t\tif (request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {\n\t\t\tenable = false;\n\t\t}\n\t\tBluetoothGattCharacteristic c = characteristic.getGattCharacteristicA();\n\t\tif (!gatt.setCharacteristicNotification(c, enable)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBluetoothGattDescriptor descriptor = c\n\t\t\t\t.getDescriptor(BleService.DESC_CCC);\n\t\tif (descriptor == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tbyte[] val_set = null;\n\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\tval_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;\n\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\tval_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;\n\t\t} else {\n\t\t\tval_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;\n\t\t}\n\t\tif (!descriptor.setValue(val_set)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn gatt.writeDescriptor(descriptor);\n\t}\n\n\t@Override\n\tpublic boolean requestWriteCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic, String remark) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,\n\t\t\t\tgatt.getDevice().getAddress(), characteristic, remark));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean writeCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null) {\n\t\t\treturn false;\n\t\t}\n\n//\t\tLog.d(\"blelib\", new String(Hex.encodeHex(characteristic.getGattCharacteristicA().getValue())));\n\t\treturn gatt\n\t\t\t\t.writeCharacteristic(characteristic.getGattCharacteristicA());\n\t}\n\n\t@Override\n\tpublic boolean requestConnect(String address) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt != null && gatt.getServices().size() == 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getBTAdapterMacAddr() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.getAddress();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean requestIndication(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_INDICATION, gatt.getDevice()\n\t\t\t\t\t\t.getAddress(), characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean requestStopNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGatt gatt = mBluetoothGatts.get(address);\n\t\tif (gatt == null || characteristic == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_NOTIFICATION, gatt.getDevice()\n\t\t\t\t\t\t.getAddress(), characteristic));\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/BleGattCharacteristic.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.annotation.SuppressLint;\n\nimport com.stag.bluetooth.extend.BleService.BLESDK;\n\nimport java.util.UUID;\n\n@SuppressLint(\"NewApi\")\npublic class BleGattCharacteristic {\n\n\tpublic static final int PROPERTY_READ = 2;\n\tpublic static final int PROPERTY_WRITE = 8;\n\tpublic static final int PROPERTY_NOTIFY = 16;\n\tpublic static final int PROPERTY_INDICATE = 32;\n\n\t/**\n\t * Characteristic value format type uint8\n\t */\n\tpublic static final int FORMAT_UINT8 = 0x11;\n\n\t/**\n\t * Characteristic value format type uint16\n\t */\n\tpublic static final int FORMAT_UINT16 = 0x12;\n\n\t/**\n\t * Characteristic value format type uint24 Note: this is not a standard data\n\t * type!\n\t */\n\tpublic static final int FORMAT_UINT24 = 0x13;\n\n\t/**\n\t * Characteristic value format type uint32\n\t */\n\tpublic static final int FORMAT_UINT32 = 0x14;\n\n\t/**\n\t * Characteristic value format type sint8\n\t */\n\tpublic static final int FORMAT_SINT8 = 0x21;\n\n\t/**\n\t * Characteristic value format type sint16\n\t */\n\tpublic static final int FORMAT_SINT16 = 0x22;\n\n\t/**\n\t * Characteristic value format type sint32\n\t */\n\tpublic static final int FORMAT_SINT32 = 0x24;\n\n\t/**\n\t * Characteristic value format type sfloat (16-bit float)\n\t */\n\tpublic static final int FORMAT_SFLOAT = 0x32;\n\n\t/**\n\t * Characteristic value format type float (32-bit float)\n\t */\n\tpublic static final int FORMAT_FLOAT = 0x34;\n\n\tprivate android.bluetooth.BluetoothGattCharacteristic mGattCharacteristicA;\n\tprivate com.broadcom.bt.gatt.BluetoothGattCharacteristic mGattCharacteristicB;\n\tprivate com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic mGattCharacteristicS;\n\tprivate BLESDK mBleSDK;\n\tprivate String name;\n\n\tpublic BleGattCharacteristic(android.bluetooth.BluetoothGattCharacteristic c) {\n\t\tmBleSDK = BLESDK.ANDROID;\n\t\tsetGattCharacteristicA(c);\n\t\tinitInfo();\n\t}\n\n\tpublic BleGattCharacteristic(\n\t\t\tcom.broadcom.bt.gatt.BluetoothGattCharacteristic c) {\n\t\tmBleSDK = BLESDK.BROADCOM;\n\t\tsetGattCharacteristicB(c);\n\t}\n\n\tpublic BleGattCharacteristic(\n\t\t\tcom.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c) {\n\t\tmBleSDK = BLESDK.SAMSUNG;\n\t\tsetGattCharacteristicS(c);\n\t}\n\n\tprivate void initInfo() {\n\t\tname = \"Unknown characteristic\";\n\t}\n\n\tpublic UUID getUuid() {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().getUuid();\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn getGattCharacteristicB().getUuid();\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn getGattCharacteristicS().getUuid();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected android.bluetooth.BluetoothGattCharacteristic getGattCharacteristicA() {\n\t\treturn mGattCharacteristicA;\n\t}\n\n\tpublic int getProperties() {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().getProperties();\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn getGattCharacteristicB().getProperties();\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn getGattCharacteristicS().getProperties();\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tprotected com.broadcom.bt.gatt.BluetoothGattCharacteristic getGattCharacteristicB() {\n\t\treturn mGattCharacteristicB;\n\t}\n\n\tprotected void setGattCharacteristicB(\n\t\t\tcom.broadcom.bt.gatt.BluetoothGattCharacteristic mBCGattCharacteristic) {\n\t\tthis.mGattCharacteristicB = mBCGattCharacteristic;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic boolean setValue(byte[] val) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().setValue(val);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.setValue(val);\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.setValue(val);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic byte[] getValue() {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().getValue();\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.getValue();\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.getValue();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic boolean setValue(int value, int formatType, int offset) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().setValue(value, formatType, offset);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.setValue(value, formatType, offset);\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.setValue(value, formatType, offset);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic boolean setValue(int mantissa, int exponent, int formatType,\n\t\t\tint offset) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().setValue(mantissa, exponent,\n\t\t\t\t\tformatType, offset);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.setValue(mantissa, exponent,\n\t\t\t\t\tformatType, offset);\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.setValue(mantissa, exponent,\n\t\t\t\t\tformatType, offset);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic boolean setValue(String value) {\n\t\treturn setValue(value.getBytes());\n\t}\n\n\tpublic String getStringValue(int offset) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().getStringValue(offset);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.getStringValue(offset);\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.getStringValue(offset);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic Float getFloatValue(int formatType, int offset) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn getGattCharacteristicA().getFloatValue(formatType, offset);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattCharacteristicS.getFloatValue(formatType, offset);\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattCharacteristicB.getFloatValue(formatType, offset);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic Integer getIntValue(int formatType, int offset) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\tif (formatType == FORMAT_UINT24) {\n\t\t\t\tbyte[] value = getGattCharacteristicA().getValue();\n\t\t\t\treturn byte2uint24(offset, value);\n\t\t\t} else {\n\t\t\t\treturn getGattCharacteristicA().getIntValue(formatType, offset);\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\tif (formatType == FORMAT_UINT24) {\n\t\t\t\tbyte[] value = mGattCharacteristicS.getValue();\n\t\t\t\treturn byte2uint24(offset, value);\n\t\t\t} else {\n\t\t\t\treturn mGattCharacteristicS.getIntValue(formatType, offset);\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\tif (formatType == FORMAT_UINT24) {\n\t\t\t\tbyte[] value = mGattCharacteristicB.getValue();\n\t\t\t\treturn byte2uint24(offset, value);\n\t\t\t} else {\n\t\t\t\treturn mGattCharacteristicB.getIntValue(formatType, offset);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate Integer byte2uint24(int offset, byte[] value) {\n\t\tif ((offset + 3) > value.length)\n\t\t\treturn null;\n\t\treturn Integer.valueOf((value[offset] & 0xFF)\n\t\t\t\t| (value[offset + 1] & 0xFF) << 8\n\t\t\t\t| (value[offset + 2] & 0xFF) << 16);\n\t}\n\n\tprotected com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic getGattCharacteristicS() {\n\t\treturn mGattCharacteristicS;\n\t}\n\n\tprotected void setGattCharacteristicS(\n\t\t\tcom.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic mSSGattCharacteristic) {\n\t\tthis.mGattCharacteristicS = mSSGattCharacteristic;\n\t}\n\n\tprotected void setGattCharacteristicA(\n\t\t\tandroid.bluetooth.BluetoothGattCharacteristic mGattCharacteristicA) {\n\t\tthis.mGattCharacteristicA = mGattCharacteristicA;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/BleGattService.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.annotation.SuppressLint;\nimport android.bluetooth.BluetoothGattCharacteristic;\n\nimport com.stag.bluetooth.extend.BleService.BLESDK;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@SuppressLint(\"NewApi\")\npublic class BleGattService {\n\n\tprivate BLESDK mBleSDK;\n\tprivate com.samsung.android.sdk.bt.gatt.BluetoothGattService mGattServiceS;\n\tprivate com.broadcom.bt.gatt.BluetoothGattService mGattServiceB;\n\tprivate android.bluetooth.BluetoothGattService mGattServiceA;\n\tprivate String mName;\n\n\tpublic BleGattService(com.samsung.android.sdk.bt.gatt.BluetoothGattService s) {\n\t\tmBleSDK = BLESDK.SAMSUNG;\n\t\tmGattServiceS = s;\n\t\tinitInfo();\n\t}\n\n\tpublic BleGattService(com.broadcom.bt.gatt.BluetoothGattService s) {\n\t\tmBleSDK = BLESDK.BROADCOM;\n\t\tmGattServiceB = s;\n\t\tinitInfo();\n\t}\n\n\tpublic BleGattService(android.bluetooth.BluetoothGattService s) {\n\t\tmBleSDK = BLESDK.ANDROID;\n\t\tmGattServiceA = s;\n\t\tinitInfo();\n\t}\n\n\tprivate void initInfo() {\n\t\tmName = \"Unknown Service\";\n\t}\n\n\tpublic UUID getUuid() {\n\t\tif (mBleSDK == BLESDK.BROADCOM) {\n\t\t\treturn mGattServiceB.getUuid();\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\treturn mGattServiceS.getUuid();\n\t\t} else if (mBleSDK == BLESDK.ANDROID) {\n\t\t\treturn mGattServiceA.getUuid();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic List<BleGattCharacteristic> getCharacteristics() {\n\t\tArrayList<BleGattCharacteristic> list = new ArrayList<BleGattCharacteristic>();\n\t\tif (mBleSDK == BLESDK.BROADCOM) {\n\t\t\tfor (com.broadcom.bt.gatt.BluetoothGattCharacteristic c : mGattServiceB\n\t\t\t\t\t.getCharacteristics()) {\n\t\t\t\tlist.add(new BleGattCharacteristic(c));\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\tfor (Object o : mGattServiceS.getCharacteristics()) {\n\t\t\t\tcom.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c = (com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic) o;\n\t\t\t\tlist.add(new BleGattCharacteristic(c));\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.ANDROID) {\n\t\t\tfor (BluetoothGattCharacteristic c : mGattServiceA\n\t\t\t\t\t.getCharacteristics()) {\n\t\t\t\tlist.add(new BleGattCharacteristic(c));\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\tpublic BleGattCharacteristic getCharacteristic(UUID uuid) {\n\t\tif (mBleSDK == BLESDK.ANDROID) {\n\t\t\tBluetoothGattCharacteristic c = mGattServiceA\n\t\t\t\t\t.getCharacteristic(uuid);\n\t\t\tif (c != null) {\n\t\t\t\treturn new BleGattCharacteristic(c);\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\tcom.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic c = mGattServiceS\n\t\t\t\t\t.getCharacteristic(uuid);\n\t\t\tif (c != null) {\n\t\t\t\treturn new BleGattCharacteristic(c);\n\t\t\t}\n\t\t} else if (mBleSDK == BLESDK.BROADCOM) {\n\t\t\tcom.broadcom.bt.gatt.BluetoothGattCharacteristic c = mGattServiceB\n\t\t\t\t\t.getCharacteristic(uuid);\n\t\t\tif (c != null) {\n\t\t\t\treturn new BleGattCharacteristic(c);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic void setInfo(JSONObject info) {\n\t\tif (info == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tsetName(info.getString(\"name\"));\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic String getName() {\n\t\treturn mName;\n\t}\n\n\tpublic void setName(String mName) {\n\t\tthis.mName = mName;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/BleRequest.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\npublic class BleRequest {\n\tpublic enum RequestType {\n\t\tCONNECT_GATT, DISCOVER_SERVICE, CHARACTERISTIC_NOTIFICATION, CHARACTERISTIC_INDICATION, READ_CHARACTERISTIC, READ_DESCRIPTOR, READ_RSSI, WRITE_CHARACTERISTIC, WRITE_DESCRIPTOR, CHARACTERISTIC_STOP_NOTIFICATION\n\t};\n\n\tpublic enum FailReason {\n\t\tSTART_FAILED, TIMEOUT, RESULT_FAILED\n\t}\n\n\tpublic RequestType type;\n\tpublic String address;\n\tpublic BleGattCharacteristic characteristic;\n\tpublic String remark;\n\n\tpublic BleRequest(RequestType type, String address) {\n\t\tthis.type = type;\n\t\tthis.address = address;\n\t}\n\n\tpublic BleRequest(RequestType type, String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tthis.type = type;\n\t\tthis.address = address;\n\t\tthis.characteristic = characteristic;\n\t}\n\n\tpublic BleRequest(RequestType type, String address,\n\t\t\t\t\t  BleGattCharacteristic characteristic, String remark) {\n\t\tthis.type = type;\n\t\tthis.address = address;\n\t\tthis.characteristic = characteristic;\n\t\tthis.remark = remark;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (!(o instanceof BleRequest)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBleRequest br = (BleRequest) o;\n\t\treturn (this.type == br.type && this.address.equals(br.address));\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/BleService.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.app.Service;\nimport android.bluetooth.BluetoothDevice;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.PackageManager;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.util.Log;\n\nimport com.stag.bluetooth.extend.BleRequest.FailReason;\nimport com.stag.bluetooth.extend.BleRequest.RequestType;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.Queue;\nimport java.util.UUID;\n\npublic class BleService extends Service {\n\tprivate static final String TAG = \"blelib\";\n\n\t/** Intent for broadcast */\n\tpublic static final String BLE_NOT_SUPPORTED = \"com.xtremeprog.sdk.ble.not_supported\";\n\tpublic static final String BLE_NO_BT_ADAPTER = \"com.xtremeprog.sdk.ble.no_bt_adapter\";\n\tpublic static final String BLE_STATUS_ABNORMAL = \"com.xtremeprog.sdk.ble.status_abnormal\";\n\t/**\n\t * @see BleService#bleRequestFailed\n\t */\n\tpublic static final String BLE_REQUEST_FAILED = \"com.xtremeprog.sdk.ble.request_failed\";\n\t/**\n\t * @see BleService#bleDeviceFound\n\t */\n\tpublic static final String BLE_DEVICE_FOUND = \"com.xtremeprog.sdk.ble.device_found\";\n\t/**\n\t * @see BleService#bleGattConnected\n\t */\n\tpublic static final String BLE_GATT_CONNECTED = \"com.xtremeprog.sdk.ble.gatt_connected\";\n\t/**\n\t * @see BleService#bleGattDisConnected\n\t */\n\tpublic static final String BLE_GATT_DISCONNECTED = \"com.xtremeprog.sdk.ble.gatt_disconnected\";\n\t/**\n\t * @see BleService#bleServiceDiscovered\n\t */\n\tpublic static final String BLE_SERVICE_DISCOVERED = \"com.xtremeprog.sdk.ble.service_discovered\";\n\t/**\n\t * @see BleService#bleCharacteristicRead\n\t */\n\tpublic static final String BLE_CHARACTERISTIC_READ = \"com.xtremeprog.sdk.ble.characteristic_read\";\n\t/**\n\t * @see BleService#bleCharacteristicNotification\n\t */\n\tpublic static final String BLE_CHARACTERISTIC_NOTIFICATION = \"com.xtremeprog.sdk.ble.characteristic_notification\";\n\t/**\n\t * @see BleService#bleCharacteristicIndication\n\t */\n\tpublic static final String BLE_CHARACTERISTIC_INDICATION = \"com.xtremeprog.sdk.ble.characteristic_indication\";\n\t/**\n\t * @see BleService#bleCharacteristicWrite\n\t */\n\tpublic static final String BLE_CHARACTERISTIC_WRITE = \"com.xtremeprog.sdk.ble.characteristic_write\";\n\t/**\n\t * @see BleService#bleCharacteristicChanged\n\t */\n\tpublic static final String BLE_CHARACTERISTIC_CHANGED = \"com.xtremeprog.sdk.ble.characteristic_changed\";\n\n\t/** Intent extras */\n\tpublic static final String EXTRA_DEVICE = \"DEVICE\";\n\tpublic static final String EXTRA_RSSI = \"RSSI\";\n\tpublic static final String EXTRA_SCAN_RECORD = \"SCAN_RECORD\";\n\tpublic static final String EXTRA_SOURCE = \"SOURCE\";\n\tpublic static final String EXTRA_ADDR = \"ADDRESS\";\n\tpublic static final String EXTRA_CONNECTED = \"CONNECTED\";\n\tpublic static final String EXTRA_STATUS = \"STATUS\";\n\tpublic static final String EXTRA_UUID = \"UUID\";\n\tpublic static final String EXTRA_VALUE = \"VALUE\";\n\tpublic static final String EXTRA_REQUEST = \"REQUEST\";\n\tpublic static final String EXTRA_REASON = \"REASON\";\n\n\t/** Source of device entries in the device list */\n\tpublic static final int DEVICE_SOURCE_SCAN = 0;\n\tpublic static final int DEVICE_SOURCE_BONDED = 1;\n\tpublic static final int DEVICE_SOURCE_CONNECTED = 2;\n\n\tpublic static final UUID DESC_CCC = UUID\n\t\t\t.fromString(\"00002902-0000-1000-8000-00805f9b34fb\");\n\n\tpublic enum BLESDK {\n\t\tNOT_SUPPORTED, ANDROID, SAMSUNG, BROADCOM\n\t}\n\n\tprivate final IBinder mBinder = new LocalBinder();\n\tprivate BLESDK mBleSDK;\n\tprivate IBle mBle;\n\tprivate Queue<BleRequest> mRequestQueue = new LinkedList<BleRequest>();\n\tprivate BleRequest mCurrentRequest = null;\n\tprivate static final int REQUEST_TIMEOUT = 10 * 10; // total timeout =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// REQUEST_TIMEOUT *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 100ms\n\tprivate boolean mCheckTimeout = false;\n\tprivate int mElapsed = 0;\n\tprivate Thread mRequestTimeout;\n\tprivate String mNotificationAddress;\n\n\tprivate Runnable mTimeoutRunnable = new Runnable() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tLog.d(TAG, \"monitoring thread start\");\n\t\t\tmElapsed = 0;\n\t\t\ttry {\n\t\t\t\twhile (mCheckTimeout) {\n\t\t\t\t\t// Log.d(TAG, \"monitoring timeout seconds: \" + mElapsed);\n\t\t\t\t\tThread.sleep(100);\n\t\t\t\t\tmElapsed++;\n\n\t\t\t\t\tif (mElapsed > REQUEST_TIMEOUT && mCurrentRequest != null) {\n\t\t\t\t\t\tLog.d(TAG, \"-processrequest type \"\n\t\t\t\t\t\t\t\t+ mCurrentRequest.type + \" address \"\n\t\t\t\t\t\t\t\t+ mCurrentRequest.address + \" [timeout]\");\n\t\t\t\t\t\tbleRequestFailed(mCurrentRequest.address,\n\t\t\t\t\t\t\t\tmCurrentRequest.type, FailReason.TIMEOUT);\n\t\t\t\t\t\tbleStatusAbnormal(\"-processrequest type \"\n\t\t\t\t\t\t\t\t+ mCurrentRequest.type + \" address \"\n\t\t\t\t\t\t\t\t+ mCurrentRequest.address + \" [timeout]\");\n\t\t\t\t\t\tif (mBle != null) {\n\t\t\t\t\t\t\tmBle.disconnect(mCurrentRequest.address);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnew Thread(new Runnable() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tmCurrentRequest = null;\n\t\t\t\t\t\t\t\tprocessNextRequest();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, \"th-ble\").start();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tLog.d(TAG, \"monitoring thread exception\");\n\t\t\t}\n\t\t\tLog.d(TAG, \"monitoring thread stop\");\n\t\t}\n\t};\n\n\tpublic static IntentFilter getIntentFilter() {\n\t\tIntentFilter intentFilter = new IntentFilter();\n\t\tintentFilter.addAction(BLE_NOT_SUPPORTED);\n\t\tintentFilter.addAction(BLE_NO_BT_ADAPTER);\n\t\tintentFilter.addAction(BLE_STATUS_ABNORMAL);\n\t\tintentFilter.addAction(BLE_REQUEST_FAILED);\n\t\tintentFilter.addAction(BLE_DEVICE_FOUND);\n\t\tintentFilter.addAction(BLE_GATT_CONNECTED);\n\t\tintentFilter.addAction(BLE_GATT_DISCONNECTED);\n\t\tintentFilter.addAction(BLE_SERVICE_DISCOVERED);\n\t\tintentFilter.addAction(BLE_CHARACTERISTIC_READ);\n\t\tintentFilter.addAction(BLE_CHARACTERISTIC_NOTIFICATION);\n\t\tintentFilter.addAction(BLE_CHARACTERISTIC_WRITE);\n\t\tintentFilter.addAction(BLE_CHARACTERISTIC_CHANGED);\n\t\treturn intentFilter;\n\t}\n\n\t@Override\n\tpublic IBinder onBind(Intent intent) {\n\t\treturn mBinder;\n\t}\n\n\tpublic class LocalBinder extends Binder {\n\t\tpublic BleService getService() {\n\t\t\treturn BleService.this;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onCreate() {\n\t\tmBleSDK = getBleSDK();\n\t\tif (mBleSDK == BLESDK.NOT_SUPPORTED) {\n\t\t\treturn;\n\t\t}\n\n\t\tLog.d(TAG, \" \" + mBleSDK);\n\t\tif (mBleSDK == BLESDK.BROADCOM) {\n\t\t\tmBle = new BroadcomBle(this);\n\t\t} else if (mBleSDK == BLESDK.ANDROID) {\n\t\t\tmBle = new AndroidBle(this);\n\t\t} else if (mBleSDK == BLESDK.SAMSUNG) {\n\t\t\tmBle = new SamsungBle(this);\n\t\t}\n\t}\n\n\tprotected void bleNotSupported() {\n\t\tIntent intent = new Intent(BleService.BLE_NOT_SUPPORTED);\n\t\tsendBroadcast(intent);\n\t}\n\n\tprotected void bleNoBtAdapter() {\n\t\tIntent intent = new Intent(BleService.BLE_NO_BT_ADAPTER);\n\t\tsendBroadcast(intent);\n\t}\n\n\tprivate BLESDK getBleSDK() {\n\t\tif (getPackageManager().hasSystemFeature(\n\t\t\t\tPackageManager.FEATURE_BLUETOOTH_LE)) {\n\t\t\t// android 4.3\n\t\t\treturn BLESDK.ANDROID;\n\t\t}\n\n\t\tArrayList<String> libraries = new ArrayList<String>();\n\t\tfor (String i : getPackageManager().getSystemSharedLibraryNames()) {\n\t\t\tlibraries.add(i);\n\t\t}\n\n\t\tif (android.os.Build.VERSION.SDK_INT >= 17) {\n\t\t\t// android 4.2.2\n\t\t\tif (libraries.contains(\"com.samsung.android.sdk.bt\")) {\n\t\t\t\treturn BLESDK.SAMSUNG;\n\t\t\t} else if (libraries.contains(\"com.broadcom.bt\")) {\n\t\t\t\treturn BLESDK.BROADCOM;\n\t\t\t}\n\t\t}\n\n\t\tbleNotSupported();\n\t\treturn BLESDK.NOT_SUPPORTED;\n\t}\n\n\tpublic IBle getBle() {\n\t\treturn mBle;\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_DEVICE_FOUND} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_DEVICE} device {@link BluetoothDevice} <br>\n\t * {@link BleService#EXTRA_RSSI} rssi int<br>\n\t * {@link BleService#EXTRA_SCAN_RECORD} scan record byte[] <br>\n\t * {@link BleService#EXTRA_SOURCE} source int, not used now <br>\n\t */\n\tprotected void bleDeviceFound(BluetoothDevice device, int rssi,\n\t\t\tbyte[] scanRecord, int source) {\n\t\tLog.d(\"blelib\", \"[\" + device.getName() + \"] device found \"\n\t\t\t\t+ device.getAddress());\n\t\tIntent intent = new Intent(BleService.BLE_DEVICE_FOUND);\n\t\tintent.putExtra(BleService.EXTRA_DEVICE, device);\n\t\tintent.putExtra(BleService.EXTRA_RSSI, rssi);\n\t\tintent.putExtra(BleService.EXTRA_SCAN_RECORD, scanRecord);\n\t\tintent.putExtra(BleService.EXTRA_SOURCE, source);\n\t\tsendBroadcast(intent);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_GATT_CONNECTED} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_DEVICE} device {@link BluetoothDevice} <br>\n\t */\n\tprotected void bleGattConnected(BluetoothDevice device) {\n\t\tIntent intent = new Intent(BLE_GATT_CONNECTED);\n\t\tintent.putExtra(EXTRA_DEVICE, device);\n\t\tintent.putExtra(EXTRA_ADDR, device.getAddress());\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(device.getAddress(), RequestType.CONNECT_GATT, true);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_GATT_DISCONNECTED} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * \n\t * @param address\n\t */\n\tprotected void bleGattDisConnected(String address) {\n\t\tIntent intent = new Intent(BLE_GATT_DISCONNECTED);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(address, RequestType.CONNECT_GATT, false);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_SERVICE_DISCOVERED} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * \n\t * @param address\n\t */\n\tprotected void bleServiceDiscovered(String address) {\n\t\tIntent intent = new Intent(BLE_SERVICE_DISCOVERED);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(address, RequestType.DISCOVER_SERVICE, true);\n\t}\n\n\tprotected void requestProcessed(String address, RequestType requestType,\n\t\t\tboolean success) {\n\t\tif (mCurrentRequest != null && mCurrentRequest.type == requestType) {\n\t\t\tclearTimeoutThread();\n\t\t\tLog.d(TAG, \"-processrequest type \" + requestType + \" address \"\n\t\t\t\t\t+ address + \" [success: \" + success + \"]\");\n\t\t\tif (!success) {\n\t\t\t\tbleRequestFailed(mCurrentRequest.address, mCurrentRequest.type,\n\t\t\t\t\t\tFailReason.RESULT_FAILED);\n\t\t\t}\n\t\t\tnew Thread(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmCurrentRequest = null;\n\t\t\t\t\tprocessNextRequest();\n\t\t\t\t}\n\t\t\t}, \"th-ble\").start();\n\t\t}\n\t}\n\n\tprivate void clearTimeoutThread() {\n\t\tif (mRequestTimeout.isAlive()) {\n\t\t\ttry {\n\t\t\t\tmCheckTimeout = false;\n\t\t\t\tmRequestTimeout.join();\n\t\t\t\tmRequestTimeout = null;\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_CHARACTERISTIC_READ} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_UUID} characteristic uuid {@link String}<br>\n\t * {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now <br>\n\t * {@link BleService#EXTRA_VALUE} data byte[] <br>\n\t * \n\t * @param address\n\t * @param uuid\n\t * @param status\n\t * @param value\n\t */\n\tprotected void bleCharacteristicRead(String address, String uuid,\n\t\t\tint status, byte[] value) {\n\t\tIntent intent = new Intent(BLE_CHARACTERISTIC_READ);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_UUID, uuid);\n\t\tintent.putExtra(EXTRA_STATUS, status);\n\t\tintent.putExtra(EXTRA_VALUE, value);\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(address, RequestType.READ_CHARACTERISTIC, true);\n\t}\n\n\tprotected void addBleRequest(BleRequest request) {\n\t\tsynchronized (mRequestQueue) {\n\t\t\tmRequestQueue.add(request);\n\t\t\tprocessNextRequest();\n\t\t}\n\t}\n\n\tprivate synchronized void processNextRequest() {\n\t\tif (mCurrentRequest != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (mRequestQueue.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tmCurrentRequest = mRequestQueue.remove();\n\t\tLog.d(TAG, \"+processrequest type \" + mCurrentRequest.type + \" address \"\n\t\t\t\t+ mCurrentRequest.address + \" remark \" + mCurrentRequest.remark);\n\t\tstartTimeoutThread();\n\t\tboolean ret = false;\n\t\tswitch (mCurrentRequest.type) {\n\t\tcase CONNECT_GATT:\n\t\t\tret = ((IBleRequestHandler) mBle).connect(mCurrentRequest.address);\n\t\t\tbreak;\n\t\tcase DISCOVER_SERVICE:\n\t\t\tret = mBle.discoverServices(mCurrentRequest.address);\n\t\t\tbreak;\n\t\tcase CHARACTERISTIC_NOTIFICATION:\n\t\tcase CHARACTERISTIC_INDICATION:\n\t\tcase CHARACTERISTIC_STOP_NOTIFICATION:\n\t\t\tret = ((IBleRequestHandler) mBle).characteristicNotification(\n\t\t\t\t\tmCurrentRequest.address, mCurrentRequest.characteristic);\n\t\t\tbreak;\n\t\tcase READ_CHARACTERISTIC:\n\t\t\tret = ((IBleRequestHandler) mBle).readCharacteristic(\n\t\t\t\t\tmCurrentRequest.address, mCurrentRequest.characteristic);\n\t\t\tbreak;\n\t\tcase WRITE_CHARACTERISTIC:\n\t\t\tret = ((IBleRequestHandler) mBle).writeCharacteristic(\n\t\t\t\t\tmCurrentRequest.address, mCurrentRequest.characteristic);\n\t\t\tbreak;\n\t\tcase READ_DESCRIPTOR:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!ret) {\n\t\t\tclearTimeoutThread();\n\t\t\tLog.d(TAG, \"-processrequest type \" + mCurrentRequest.type\n\t\t\t\t\t+ \" address \" + mCurrentRequest.address + \" [fail start]\");\n\t\t\tbleRequestFailed(mCurrentRequest.address, mCurrentRequest.type,\n\t\t\t\t\tFailReason.START_FAILED);\n\t\t\tnew Thread(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmCurrentRequest = null;\n\t\t\t\t\tprocessNextRequest();\n\t\t\t\t}\n\t\t\t}, \"th-ble\").start();\n\t\t}\n\t}\n\n\tprivate void startTimeoutThread() {\n\t\tmCheckTimeout = true;\n\t\tmRequestTimeout = new Thread(mTimeoutRunnable);\n\t\tmRequestTimeout.start();\n\t}\n\n\tprotected BleRequest getCurrentRequest() {\n\t\treturn mCurrentRequest;\n\t}\n\n\tprotected void setCurrentRequest(BleRequest mCurrentRequest) {\n\t\tthis.mCurrentRequest = mCurrentRequest;\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_CHARACTERISTIC_NOTIFICATION} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_UUID} characteristic uuid {@link String}<br>\n\t * {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now <br>\n\t * \n\t * @param address\n\t * @param uuid\n\t * @param status\n\t */\n\tprotected void bleCharacteristicNotification(String address, String uuid,\n\t\t\tboolean isEnabled, int status) {\n\t\tIntent intent = new Intent(BLE_CHARACTERISTIC_NOTIFICATION);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_UUID, uuid);\n\t\tintent.putExtra(EXTRA_VALUE, isEnabled);\n\t\tintent.putExtra(EXTRA_STATUS, status);\n\t\tsendBroadcast(intent);\n\t\tif (isEnabled) {\n\t\t\trequestProcessed(address, RequestType.CHARACTERISTIC_NOTIFICATION,\n\t\t\t\t\ttrue);\n\t\t} else {\n\t\t\trequestProcessed(address,\n\t\t\t\t\tRequestType.CHARACTERISTIC_STOP_NOTIFICATION, true);\n\t\t}\n\t\tsetNotificationAddress(address);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_CHARACTERISTIC_INDICATION} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_UUID} characteristic uuid {@link String}<br>\n\t * {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now <br>\n\t * \n\t * @param address\n\t * @param uuid\n\t * @param status\n\t */\n\tprotected void bleCharacteristicIndication(String address, String uuid,\n\t\t\tint status) {\n\t\tIntent intent = new Intent(BLE_CHARACTERISTIC_INDICATION);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_UUID, uuid);\n\t\tintent.putExtra(EXTRA_STATUS, status);\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(address, RequestType.CHARACTERISTIC_INDICATION, true);\n\t\tsetNotificationAddress(address);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_CHARACTERISTIC_WRITE} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_UUID} characteristic uuid {@link String}<br>\n\t * {@link BleService#EXTRA_STATUS} read status {@link Integer} Not used now <br>\n\t * \n\t * @param address\n\t * @param uuid\n\t * @param status\n\t */\n\tprotected void bleCharacteristicWrite(String address, String uuid,\n\t\t\tint status) {\n\t\tIntent intent = new Intent(BLE_CHARACTERISTIC_WRITE);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_UUID, uuid);\n\t\tintent.putExtra(EXTRA_STATUS, status);\n\t\tsendBroadcast(intent);\n\t\trequestProcessed(address, RequestType.WRITE_CHARACTERISTIC, true);\n\t}\n\n\t/**\n\t * Send {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast. <br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_UUID} characteristic uuid {@link String}<br>\n\t * {@link BleService#EXTRA_VALUE} data byte[] <br>\n\t * \n\t * @param address\n\t * @param uuid\n\t * @param value\n\t */\n\tprotected void bleCharacteristicChanged(String address, String uuid,\n\t\t\tbyte[] value) {\n\t\tIntent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_UUID, uuid);\n\t\tintent.putExtra(EXTRA_VALUE, value);\n\t\tsendBroadcast(intent);\n\t}\n\n\t/**\n\t * @param reason\n\t */\n\tprotected void bleStatusAbnormal(String reason) {\n\t\tIntent intent = new Intent(BLE_STATUS_ABNORMAL);\n\t\tintent.putExtra(EXTRA_VALUE, reason);\n\t\tsendBroadcast(intent);\n\t}\n\n\t/**\n\t * Sent when BLE request failed.<br>\n\t * <br>\n\t * Data in the broadcast intent: <br>\n\t * {@link BleService#EXTRA_ADDR} device address {@link String} <br>\n\t * {@link BleService#EXTRA_REQUEST} request type\n\t * {@link BleRequest.RequestType} <br>\n\t * {@link BleService#EXTRA_REASON} fail reason {@link BleRequest.FailReason} <br>\n\t */\n\tprotected void bleRequestFailed(String address, RequestType type,\n\t\t\tFailReason reason) {\n\t\tIntent intent = new Intent(BLE_REQUEST_FAILED);\n\t\tintent.putExtra(EXTRA_ADDR, address);\n\t\tintent.putExtra(EXTRA_REQUEST, type);\n\t\tintent.putExtra(EXTRA_REASON, reason.ordinal());\n\t\tsendBroadcast(intent);\n\t}\n\n\tprotected String getNotificationAddress() {\n\t\treturn mNotificationAddress;\n\t}\n\n\tprotected void setNotificationAddress(String mNotificationAddress) {\n\t\tthis.mNotificationAddress = mNotificationAddress;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/BroadcomBle.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.bluetooth.BluetoothProfile;\n\nimport com.broadcom.bt.gatt.BluetoothGatt;\nimport com.broadcom.bt.gatt.BluetoothGattAdapter;\nimport com.broadcom.bt.gatt.BluetoothGattCallback;\nimport com.broadcom.bt.gatt.BluetoothGattCharacteristic;\nimport com.broadcom.bt.gatt.BluetoothGattDescriptor;\nimport com.broadcom.bt.gatt.BluetoothGattService;\nimport com.stag.bluetooth.extend.BleRequest.RequestType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class BroadcomBle implements IBle, IBleRequestHandler {\n\tprivate BluetoothAdapter mBtAdapter;\n\tprivate BleService mService;\n\tprivate BluetoothGatt mBluetoothGatt;\n\tprivate boolean mScanning;\n\tprivate String mAddress;\n\n\tprivate final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {\n\t\t@Override\n\t\tpublic void onAppRegistered(int status) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScanResult(BluetoothDevice device, int rssi,\n\t\t\t\tbyte[] scanRecord) {\n\t\t\tmService.bleDeviceFound(device, rssi, scanRecord,\n\t\t\t\t\tBleService.DEVICE_SOURCE_SCAN);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onConnectionStateChange(BluetoothDevice device, int status,\n\t\t\t\tint newState) {\n\t\t\tif (mBluetoothGatt == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (newState == BluetoothProfile.STATE_CONNECTED) {\n\t\t\t\tmService.bleGattConnected(device);\n\t\t\t\tmBluetoothGatt.discoverServices(device);\n\t\t\t\tmAddress = device.getAddress();\n\t\t\t} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {\n\t\t\t\tmService.bleGattDisConnected(device.getAddress());\n\t\t\t\tmAddress = null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onServicesDiscovered(BluetoothDevice device, int status) {\n\t\t\tmService.bleServiceDiscovered(device.getAddress());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicRead(\n\t\t\t\tBluetoothGattCharacteristic characteristic, int status) {\n\t\t\tif (status == BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.bleCharacteristicRead(mAddress, characteristic\n\t\t\t\t\t\t.getUuid().toString(), status, characteristic\n\t\t\t\t\t\t.getValue());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicChanged(\n\t\t\t\tBluetoothGattCharacteristic characteristic) {\n\t\t\tString address = mService.getNotificationAddress();\n\t\t\tmService.bleCharacteristicChanged(address, characteristic.getUuid()\n\t\t\t\t\t.toString(), characteristic.getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onDescriptorRead(BluetoothGattDescriptor descriptor,\n\t\t\t\tint status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tbyte[] value = descriptor.getValue();\n\t\t\tbyte[] val_set = null;\n\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\tval_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;\n\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\tval_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;\n\t\t\t} else {\n\t\t\t\tval_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;\n\t\t\t}\n\n\t\t\tif (Arrays.equals(value, val_set)) {\n\t\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), true,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\t\tmService.bleCharacteristicIndication(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), status);\n\t\t\t\t} else {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), false,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!descriptor.setValue(val_set)) {\n\t\t\t\tmService.requestProcessed(address, request.type, false);\n\t\t\t}\n\n\t\t\tmBluetoothGatt.writeDescriptor(descriptor);\n\t\t};\n\n\t\t@Override\n\t\tpublic void onDescriptorWrite(BluetoothGattDescriptor descriptor,\n\t\t\t\tint status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_INDICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {\n\t\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\t\tmService.requestProcessed(address, request.type, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), true,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\t\tmService.bleCharacteristicIndication(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), status);\n\t\t\t\t} else {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), false,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t};\n\t};\n\n\tprivate final BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {\n\t\t@Override\n\t\tpublic void onServiceConnected(int profile, BluetoothProfile proxy) {\n\t\t\tmBluetoothGatt = (BluetoothGatt) proxy;\n\t\t\tmBluetoothGatt.registerApp(mGattCallbacks);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onServiceDisconnected(int profile) {\n\t\t\tfor ( BluetoothDevice d : mBluetoothGatt.getConnectedDevices() ) {\n\t\t\t\tmBluetoothGatt.cancelConnection(d);\n\t\t\t}\n\t\t\tmBluetoothGatt = null;\n\t\t}\n\t};\n\n\tpublic BroadcomBle(BleService service) {\n\t\tmService = service;\n\t\tmBtAdapter = BluetoothAdapter.getDefaultAdapter();\n\t\tif (mBtAdapter == null) {\n\t\t\tmService.bleNoBtAdapter();\n\t\t\treturn;\n\t\t}\n\t\tBluetoothGattAdapter.getProfileProxy(mService, mProfileServiceListener,\n\t\t\t\tBluetoothGattAdapter.GATT);\n\t}\n\n\t@Override\n\tpublic void startScan() {\n\t\tif (mScanning) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (mBluetoothGatt == null) {\n\t\t\tmScanning = false;\n\t\t\treturn;\n\t\t}\n\n\t\tmScanning = true;\n\t\tmBluetoothGatt.startScan();\n\t}\n\n\t@Override\n\tpublic void stopScan() {\n\t\tif (!mScanning || mBluetoothGatt == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tmScanning = false;\n\t\tmBluetoothGatt.stopScan();\n\t}\n\n\t@Override\n\tpublic boolean adapterEnabled() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.isEnabled();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean connect(String address) {\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\treturn mBluetoothGatt.connect(device, false);\n\t}\n\n\t@Override\n\tpublic void disconnect(String address) {\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\tmBluetoothGatt.cancelConnection(device);\n\t}\n\n\t@Override\n\tpublic ArrayList<BleGattService> getServices(String address) {\n\t\tArrayList<BleGattService> list = new ArrayList<BleGattService>();\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\tList<BluetoothGattService> services = mBluetoothGatt\n\t\t\t\t.getServices(device);\n\t\tfor (BluetoothGattService s : services) {\n\t\t\tlist.add(new BleGattService(s));\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic boolean requestReadCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,\n\t\t\t\taddress, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean discoverServices(String address) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean readCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tif (characteristic.getGattCharacteristicB() != null) {\n\t\t\treturn mBluetoothGatt.readCharacteristic(characteristic\n\t\t\t\t\t.getGattCharacteristicB());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic BleGattService getService(String address, UUID uuid) {\n\t\tBluetoothGattService service = mBluetoothGatt.getService(\n\t\t\t\tmBtAdapter.getRemoteDevice(address), uuid);\n\t\tif (service == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn new BleGattService(service);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean requestCharacteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_NOTIFICATION, address,\n\t\t\t\tcharacteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean characteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBleRequest request = mService.getCurrentRequest();\n\t\tBluetoothGattCharacteristic b = characteristic.getGattCharacteristicB();\n\n\t\tboolean enable = true;\n\t\tif (request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {\n\t\t\tenable = false;\n\t\t}\n\t\tif (!mBluetoothGatt.setCharacteristicNotification(b, enable)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBluetoothGattDescriptor descriptor = b\n\t\t\t\t.getDescriptor(BleService.DESC_CCC);\n\t\tif (descriptor == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn mBluetoothGatt.readDescriptor(descriptor);\n\t}\n\n\t@Override\n\tpublic boolean requestWriteCharacteristic(String address,\n\t\t\t\t\t\t\t\t\t\t\t  BleGattCharacteristic characteristic, String remark) {\n\t\tmService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,\n\t\t\t\taddress, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean writeCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\treturn mBluetoothGatt.writeCharacteristic(characteristic\n\t\t\t\t.getGattCharacteristicB());\n\t}\n\n\t@Override\n\tpublic boolean requestConnect(String address) {\n\t\tif (mAddress != null) {\n\t\t\treturn false;\n\t\t}\n\t\tmService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getBTAdapterMacAddr() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.getAddress();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean requestIndication(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_INDICATION, address, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean requestStopNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_STOP_NOTIFICATION, address,\n\t\t\t\tcharacteristic));\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/IBle.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport java.util.ArrayList;\nimport java.util.UUID;\n\npublic interface IBle {\n\n\tpublic String getBTAdapterMacAddr();\n\n\t/**\n\t * Will receive broadcast {@link BleService#BLE_DEVICE_FOUND} if device\n\t * found.\n\t */\n\tpublic void startScan();\n\n\t/**\n\t * Stop BLE scan.\n\t */\n\tpublic void stopScan();\n\n\t/**\n\t * Check if bluetooth adapter is enabled.\n\t * \n\t * @return enabled\n\t */\n\tpublic boolean adapterEnabled();\n\n\t/**\n\t * Disconnect BLE device. Will receive\n\t * {@link BleService#BLE_GATT_DISCONNECTED} broadcast if device\n\t * disconnected.\n\t * \n\t * @param address\n\t *            BLE device address.\n\t */\n\tpublic void disconnect(String address);\n\n\t/**\n\t * Discover BLE services. Will receive\n\t * {@link BleService#BLE_SERVICE_DISCOVERED} broadcast if device service\n\t * discovered.\n\t * \n\t * @param address\n\t * @return\n\t */\n\tpublic boolean discoverServices(String address);\n\n\t/**\n\t * Get discovered services for BLE device. Call this function after\n\t * {@link BleService#BLE_SERVICE_DISCOVERED} broadcast is received.\n\t * \n\t * @param address\n\t * @return List of {@link BleGattService}\n\t */\n\tpublic ArrayList<BleGattService> getServices(String address);\n\n\t/**\n\t * Get discovered service by uuid. Call this function after\n\t * {@link BleService#BLE_SERVICE_DISCOVERED} broadcast is received.\n\t * \n\t * @param address\n\t * @param uuid\n\t * @return {@link BleGattService}\n\t */\n\tpublic BleGattService getService(String address, UUID uuid);\n\n\t/**\n\t * Request to connect a BLE device by address. Will receive\n\t * {@link BleService#BLE_GATT_CONNECTED} broadcast if device connected.\n\t * \n\t * @param address\n\t * @return if request be inserted into queue successfully.\n\t */\n\tpublic boolean requestConnect(String address);\n\n\t/**\n\t * Request to read characteristic. Will receive\n\t * {@link BleService#BLE_CHARACTERISTIC_READ} broadcast if characteristic\n\t * read.\n\t * \n\t * @param address\n\t * @param characteristic\n\t *            Get characteristic from {@link BleGattService}\n\t * @return if request be inserted into queue successfully.\n\t */\n\tpublic boolean requestReadCharacteristic(String address,\n\t\t\t\t\t\t\t\t\t\t\t BleGattCharacteristic characteristic);\n\n\t/**\n\t * Request characteristic notification. Will receive\n\t * {@link BleService#BLE_CHARACTERISTIC_NOTIFICATION} broadcast if\n\t * notification set OK. When the characteristic's value changed,\n\t * {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast will be received\n\t * also.\n\t * \n\t * @param address\n\t * @param characteristic\n\t *            Get characteristic from {@link BleGattService}\n\t * @return if request be inserted into queue successfully.\n\t */\n\tpublic boolean requestCharacteristicNotification(String address,\n\t\t\t\t\t\t\t\t\t\t\t\t\t BleGattCharacteristic characteristic);\n\n\tpublic boolean requestStopNotification(String address,\n\t\t\t\t\t\t\t\t\t\t   BleGattCharacteristic characteristic);\n\n\t/**\n\t * Request characteristic indication. Will receive\n\t * {@link BleService#BLE_CHARACTERISTIC_INDICATION} broadcast if indication\n\t * set OK. When the characteristic's value changed,\n\t * {@link BleService#BLE_CHARACTERISTIC_CHANGED} broadcast will be received\n\t * also.\n\t * \n\t * @param address\n\t * @param characteristic\n\t *            Get characteristic from {@link BleGattService}\n\t * @return if request be inserted into queue successfully.\n\t */\n\tpublic boolean requestIndication(String address,\n\t\t\t\t\t\t\t\t\t BleGattCharacteristic characteristic);\n\n\t/**\n\t * Request write characteristic value. Will receive\n\t * {@link BleService#BLE_CHARACTERISTIC_WRITE} broadcast if characteristic\n\t * value be written.\n\t * \n\t * @param address\n\t * @param characteristic\n\t *            Get characteristic from {@link BleGattService}\n\t * @param remark\n\t *            For debug purpose.\n\t * @return if request be inserted into queue successfully.\n\t */\n\tpublic boolean requestWriteCharacteristic(String address,\n\t\t\t\t\t\t\t\t\t\t\t  BleGattCharacteristic characteristic, String remark);\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/IBleRequestHandler.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\npublic interface IBleRequestHandler {\n\n\tpublic boolean connect(String address);\n\n\t/**\n\t * @param address\n\t * @param characteristic\n\t * @return\n\t */\n\tpublic boolean readCharacteristic(String address,\n\t\t\t\t\t\t\t\t\t  BleGattCharacteristic characteristic);\n\n\t/**\n\t * @param address\n\t * @param characteristic\n\t * @return\n\t */\n\tpublic boolean characteristicNotification(String address,\n\t\t\t\t\t\t\t\t\t\t\t  BleGattCharacteristic characteristic);\n\n\t/**\n\t * @param address\n\t * @param characteristic\n\t * @return\n\t */\n\tpublic boolean writeCharacteristic(String address,\n\t\t\t\t\t\t\t\t\t   BleGattCharacteristic characteristic);\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/extend/SamsungBle.java",
    "content": "/**\n * This XPG software is supplied to you by Xtreme Programming Group, Inc.\n * (\"XPG\") in consideration of your agreement to the following terms, and your\n * use, installation, modification or redistribution of this XPG software\n * constitutes acceptance of these terms.� If you do not agree with these terms,\n * please do not use, install, modify or redistribute this XPG software.\n * \n * In consideration of your agreement to abide by the following terms, and\n * subject to these terms, XPG grants you a non-exclusive license, under XPG's\n * copyrights in this original XPG software (the \"XPG Software\"), to use and\n * redistribute the XPG Software, in source and/or binary forms; provided that\n * if you redistribute the XPG Software, with or without modifications, you must\n * retain this notice and the following text and disclaimers in all such\n * redistributions of the XPG Software. Neither the name, trademarks, service\n * marks or logos of XPG Inc. may be used to endorse or promote products derived\n * from the XPG Software without specific prior written permission from XPG.�\n * Except as expressly stated in this notice, no other rights or licenses,\n * express or implied, are granted by XPG herein, including but not limited to\n * any patent rights that may be infringed by your derivative works or by other\n * works in which the XPG Software may be incorporated.\n * \n * The XPG Software is provided by XPG on an \"AS IS\" basis.� XPG MAKES NO\n * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\n * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE, REGARDING THE XPG SOFTWARE OR ITS USE AND OPERATION ALONE OR IN\n * COMBINATION WITH YOUR PRODUCTS.\n * \n * IN NO EVENT SHALL XPG BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION\n * AND/OR DISTRIBUTION OF THE XPG SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER\n * THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR\n * OTHERWISE, EVEN IF XPG HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n * \n * ABOUT XPG: Established since June 2005, Xtreme Programming Group, Inc. (XPG)\n * is a digital solutions company based in the United States and China. XPG\n * integrates cutting-edge hardware designs, mobile applications, and cloud\n * computing technologies to bring innovative products to the marketplace. XPG's\n * partners and customers include global leading corporations in semiconductor,\n * home appliances, health/wellness electronics, toys and games, and automotive\n * industries. Visit www.xtremeprog.com for more information.\n * \n * Copyright (C) 2013 Xtreme Programming Group, Inc. All Rights Reserved.\n */\n\npackage com.stag.bluetooth.extend;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.bluetooth.BluetoothProfile;\nimport android.util.Log;\n\nimport com.samsung.android.sdk.bt.gatt.BluetoothGatt;\nimport com.samsung.android.sdk.bt.gatt.BluetoothGattAdapter;\nimport com.samsung.android.sdk.bt.gatt.BluetoothGattCallback;\nimport com.samsung.android.sdk.bt.gatt.BluetoothGattCharacteristic;\nimport com.samsung.android.sdk.bt.gatt.BluetoothGattDescriptor;\nimport com.samsung.android.sdk.bt.gatt.BluetoothGattService;\nimport com.stag.bluetooth.extend.BleRequest.RequestType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class SamsungBle implements IBle, IBleRequestHandler {\n\n\tprotected static final String TAG = \"blelib\";\n\n\tprivate BluetoothAdapter mBtAdapter;\n\tprivate BleService mService;\n\tprivate BluetoothGatt mBluetoothGatt;\n\tprivate boolean mScanning;\n\n\tprivate final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {\n\t\t@Override\n\t\tpublic void onAppRegistered(int status) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScanResult(BluetoothDevice device, int rssi,\n\t\t\t\tbyte[] scanRecord) {\n\t\t\tmService.bleDeviceFound(device, rssi, scanRecord,\n\t\t\t\t\tBleService.DEVICE_SOURCE_SCAN);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onConnectionStateChange(BluetoothDevice device, int status,\n\t\t\t\tint newState) {\n\t\t\tif (mBluetoothGatt == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tdisconnect(device.getAddress());\n\t\t\t\tmService.bleGattDisConnected(device.getAddress());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (newState == BluetoothProfile.STATE_CONNECTED) {\n\t\t\t\tmService.bleGattConnected(device);\n\t\t\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\t\t\tRequestType.DISCOVER_SERVICE, device.getAddress()));\n\t\t\t} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {\n\t\t\t\tmService.bleGattDisConnected(device.getAddress());\n\t\t\t\tdisconnect(device.getAddress());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onServicesDiscovered(BluetoothDevice device, int status) {\n\t\t\tString address = device.getAddress();\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tdisconnect(address);\n\t\t\t\tmService.bleGattDisConnected(address);\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.DISCOVER_SERVICE, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmService.bleServiceDiscovered(device.getAddress());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicRead(\n\t\t\t\tBluetoothGattCharacteristic characteristic, int status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.READ_CHARACTERISTIC, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmService.bleCharacteristicRead(address, characteristic.getUuid()\n\t\t\t\t\t.toString(), status, characteristic.getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicChanged(\n\t\t\t\tBluetoothGattCharacteristic characteristic) {\n\t\t\tLog.d(TAG, \"onCharacteristicChanged\");\n\t\t\tString address = mService.getNotificationAddress();\n\t\t\tmService.bleCharacteristicChanged(address, characteristic.getUuid()\n\t\t\t\t\t.toString(), characteristic.getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCharacteristicWrite(\n\t\t\t\tBluetoothGattCharacteristic characteristic, int status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\tmService.requestProcessed(address,\n\t\t\t\t\t\tRequestType.WRITE_CHARACTERISTIC, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmService.bleCharacteristicWrite(address, characteristic.getUuid()\n\t\t\t\t\t.toString(), status);\n\t\t};\n\n\t\t@Override\n\t\tpublic void onDescriptorRead(BluetoothGattDescriptor descriptor,\n\t\t\t\tint status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tbyte[] value = descriptor.getValue();\n\t\t\tbyte[] val_set = null;\n\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\tval_set = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;\n\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\tval_set = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;\n\t\t\t} else {\n\t\t\t\tval_set = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;\n\t\t\t}\n\n\t\t\tif (Arrays.equals(value, val_set)) {\n\t\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), true,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\t\tmService.bleCharacteristicIndication(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), status);\n\t\t\t\t} else {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), false,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!descriptor.setValue(val_set)) {\n\t\t\t\tmService.requestProcessed(address, request.type, false);\n\t\t\t}\n\n\t\t\tmBluetoothGatt.writeDescriptor(descriptor);\n\t\t};\n\n\t\t@Override\n\t\tpublic void onDescriptorWrite(BluetoothGattDescriptor descriptor,\n\t\t\t\tint status) {\n\t\t\tBleRequest request = mService.getCurrentRequest();\n\t\t\tString address = request.address;\n\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_INDICATION\n\t\t\t\t\t|| request.type == RequestType.CHARACTERISTIC_STOP_NOTIFICATION) {\n\t\t\t\tif (status != BluetoothGatt.GATT_SUCCESS) {\n\t\t\t\t\tmService.requestProcessed(address, request.type, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (request.type == RequestType.CHARACTERISTIC_NOTIFICATION) {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), true,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t} else if (request.type == RequestType.CHARACTERISTIC_INDICATION) {\n\t\t\t\t\tmService.bleCharacteristicIndication(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), status);\n\t\t\t\t} else {\n\t\t\t\t\tmService.bleCharacteristicNotification(address, descriptor\n\t\t\t\t\t\t\t.getCharacteristic().getUuid().toString(), false,\n\t\t\t\t\t\t\tstatus);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t};\n\t};\n\n\tprivate final BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {\n\t\t@Override\n\t\tpublic void onServiceConnected(int profile, BluetoothProfile proxy) {\n\t\t\tmBluetoothGatt = (BluetoothGatt) proxy;\n\t\t\tmBluetoothGatt.registerApp(mGattCallbacks);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onServiceDisconnected(int profile) {\n\t\t\tmBluetoothGatt = null;\n\t\t}\n\t};\n\n\tpublic SamsungBle(BleService service) {\n\t\tmService = service;\n\t\tmBtAdapter = BluetoothAdapter.getDefaultAdapter();\n\t\tif (mBtAdapter == null) {\n\t\t\tmService.bleNoBtAdapter();\n\t\t\treturn;\n\t\t}\n\t\tBluetoothGattAdapter.getProfileProxy(mService, mProfileServiceListener,\n\t\t\t\tBluetoothGattAdapter.GATT);\n\t}\n\n\t@Override\n\tpublic void startScan() {\n\t\tif (mScanning) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (mBluetoothGatt == null) {\n\t\t\tmScanning = false;\n\t\t\treturn;\n\t\t}\n\n\t\tmScanning = true;\n\t\tmBluetoothGatt.startScan();\n\t}\n\n\t@Override\n\tpublic void stopScan() {\n\t\tif (!mScanning || mBluetoothGatt == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tmScanning = false;\n\t\tmBluetoothGatt.stopScan();\n\t}\n\n\t@Override\n\tpublic boolean adapterEnabled() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.isEnabled();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean connect(String address) {\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\treturn mBluetoothGatt.connect(device, false);\n\t}\n\n\t@Override\n\tpublic void disconnect(String address) {\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\tmBluetoothGatt.cancelConnection(device);\n\t}\n\n\t@Override\n\tpublic ArrayList<BleGattService> getServices(String address) {\n\t\tArrayList<BleGattService> list = new ArrayList<BleGattService>();\n\t\tBluetoothDevice device = mBtAdapter.getRemoteDevice(address);\n\t\tList<BluetoothGattService> services = mBluetoothGatt\n\t\t\t\t.getServices(device);\n\t\tfor (BluetoothGattService s : services) {\n\t\t\tlist.add(new BleGattService(s));\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic boolean requestReadCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(RequestType.READ_CHARACTERISTIC,\n\t\t\t\taddress, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean discoverServices(String address) {\n\t\treturn mBluetoothGatt.discoverServices(mBtAdapter\n\t\t\t\t.getRemoteDevice(address));\n\t}\n\n\t@Override\n\tpublic boolean readCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\treturn mBluetoothGatt.readCharacteristic(characteristic\n\t\t\t\t.getGattCharacteristicS());\n\t}\n\n\t@Override\n\tpublic BleGattService getService(String address, UUID uuid) {\n\t\tBluetoothGattService service = mBluetoothGatt.getService(\n\t\t\t\tmBtAdapter.getRemoteDevice(address), uuid);\n\t\tif (service == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn new BleGattService(service);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean requestCharacteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_NOTIFICATION, address,\n\t\t\t\tcharacteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean characteristicNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tBluetoothGattCharacteristic c = characteristic.getGattCharacteristicS();\n\n\t\tif (!mBluetoothGatt.setCharacteristicNotification(c, true)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBluetoothGattDescriptor descriptor = c\n\t\t\t\t.getDescriptor(BleService.DESC_CCC);\n\t\tif (descriptor == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn mBluetoothGatt.readDescriptor(descriptor);\n\t}\n\n\t@Override\n\tpublic boolean requestWriteCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic, String remark) {\n\t\tmService.addBleRequest(new BleRequest(RequestType.WRITE_CHARACTERISTIC,\n\t\t\t\taddress, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean writeCharacteristic(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\treturn mBluetoothGatt.writeCharacteristic(characteristic\n\t\t\t\t.getGattCharacteristicS());\n\t}\n\n\t@Override\n\tpublic boolean requestConnect(String address) {\n\t\tmService.addBleRequest(new BleRequest(RequestType.CONNECT_GATT, address));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getBTAdapterMacAddr() {\n\t\tif (mBtAdapter != null) {\n\t\t\treturn mBtAdapter.getAddress();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean requestIndication(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_INDICATION, address, characteristic));\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean requestStopNotification(String address,\n\t\t\tBleGattCharacteristic characteristic) {\n\t\tmService.addBleRequest(new BleRequest(\n\t\t\t\tRequestType.CHARACTERISTIC_STOP_NOTIFICATION, address,\n\t\t\t\tcharacteristic));\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/helper/BleHelper.java",
    "content": "package com.stag.bluetooth.helper;\n\nimport android.annotation.TargetApi;\nimport android.bluetooth.BluetoothGatt;\nimport android.bluetooth.BluetoothGattCallback;\nimport android.bluetooth.BluetoothGattCharacteristic;\nimport android.bluetooth.BluetoothGattDescriptor;\nimport android.bluetooth.BluetoothGattService;\nimport android.bluetooth.BluetoothProfile;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Build;\nimport android.os.IBinder;\n\nimport com.stag.bluetooth.extend.BleService;\nimport com.stag.bluetooth.extend.IBle;\nimport com.stag.bluetooth.protocol.Protocol;\nimport com.stag.bluetooth.util.ByteUtils;\nimport com.stag.bluetooth.util.Logs;\n\nimport java.util.UUID;\n\n/**\n * Created by Administrator on 2016/11/14.\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)\npublic final class BleHelper extends BluetoothHelper {\n\n    private static final String TAG = \"LPQ\";\n    public final static UUID DEFAULT_DESCRIPTOR_UUID = UUID.fromString(\"00002902-0000-1000-8000-00805f9b34fb\");\n    public final static String BLE_SCAN_FINISH = \"BLE_SCAN_FINISH\";\n    private final static int SCAN_DURATION = 10000;\n    private final static short MAX_WRITE_DESCRIPTOR_TIME = 3000;\n    private final static int MAX_SINGLE_SZIE = 512;\n    private final static int DEFAULT_SINGLE_SZIE = 20;\n    private static BleHelper instance;\n    private BluetoothGatt mGatt;\n    private BluetoothGattCharacteristic mCRead;\n    private BluetoothGattCharacteristic mCWrite;\n    private Thread timeoutCountThread;  //超时计数\n    private boolean isResumeTimeoutCount, isResumeScanCount;\n    private ServiceConnection connBle;\n    private BleService mBleService;\n    private IBle mBle;\n    private Thread scanCountThread;     //扫描计时\n    private int mSingleSize = DEFAULT_SINGLE_SZIE;\n    private boolean mSetHeightSpeedModeResult;\n    private boolean mWriteDescriptorResult;\n    private boolean sendResult;//是否发生成功\n\n    public static BleHelper getInstance(Context context) {\n        if (instance == null) {\n            synchronized (BleHelper.class) {\n                instance = new BleHelper(context);\n            }\n        }\n        return instance;\n    }\n\n    private BleHelper(Context context) {\n        super(context);\n    }\n\n    @Override\n    public void startScan() {\n        if (mBle == null) {\n            bindBleService();\n        } else {\n            mBle.startScan();\n        }\n    }\n\n    @Override\n    public void stopScan() {\n        if (mBle != null) {\n            mBle.stopScan();\n        }\n        stopScanCount();\n    }\n\n    @Override\n    public void connect(final String address) {\n        mMaxTime = 0;\n        configHandler();\n//        BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);\n//        BluetoothAdapter adapter = bluetoothManager.getAdapter();\n        setConnecting(true);\n        mDevice = mBluetoothAdapter.getRemoteDevice(address);\n        mCWrite = null;\n        mCRead = null;\n        mSingleSize = DEFAULT_SINGLE_SZIE;\n        mGatt = mDevice.connectGatt(mContext, false, mCallBack);\n        if (mGatt == null) {\n            Logs.d(TAG, \"GATT NULL，FAIL\");\n            handleConnectEvent(isConnecting(), false);\n        } else {\n            Logs.d(TAG, \"start connect... \" + address);\n            startTimeoutCount();\n        }\n    }\n\n    @Override\n    public void disconnect() {\n        handleConnectEvent(false, false);\n    }\n\n    private void bindBleService() {\n        connBle = new ServiceConnection() {\n            @Override\n            public void onServiceConnected(ComponentName name, IBinder service) {\n                mBleService = ((BleService.LocalBinder) service).getService();\n                mBle = mBleService.getBle();\n                if (mBle != null && mBle.adapterEnabled()) {\n                    mBle.startScan();\n                }\n            }\n\n            @Override\n            public void onServiceDisconnected(ComponentName name) {\n                mBleService = null;\n                mBle = null;\n            }\n        };\n        Intent intent = new Intent(mContext, BleService.class);\n        mContext.bindService(intent, connBle, Context.BIND_AUTO_CREATE);\n    }\n\n    /**\n     * 开始扫描计时\n     */\n    private void startScanCount() {\n        isResumeScanCount = true;\n        scanCountThread = new Thread() {\n            @Override\n            public void run() {\n                super.run();\n                try {\n                    Thread.sleep(SCAN_DURATION);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                if (isResumeScanCount)\n                    stopScan();\n            }\n        };\n        scanCountThread.start();\n    }\n\n    /**\n     * 停止扫描计时\n     */\n    private void stopScanCount() {\n        if (scanCountThread != null) {\n            isResumeScanCount = false;\n            scanCountThread.interrupt();\n            scanCountThread = null;\n        }\n        mContext.sendBroadcast(new Intent(BLE_SCAN_FINISH));\n    }\n\n    /**\n     * 开始连接超时计数\n     */\n    private void startTimeoutCount() {\n        isResumeTimeoutCount = true;\n        timeoutCountThread = new Thread() {\n            @Override\n            public void run() {\n                super.run();\n                try {\n                    Thread.sleep(CONNECT_TIMEOUT);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                Logs.d(TAG, \"timeout:\" + !isConnected + \" isResumeTimeoutCount:\" + isResumeTimeoutCount);\n                if (!isConnected && isResumeTimeoutCount)\n                    handleConnectEvent(isConnecting(), false);\n            }\n        };\n        timeoutCountThread.start();\n    }\n\n    /**\n     * 停止连接超时计数\n     */\n    private void stopTimeoutCount() {\n        if (timeoutCountThread != null) {\n            isResumeTimeoutCount = false;\n            timeoutCountThread.interrupt();\n            timeoutCountThread = null;\n        }\n    }\n\n    @Override\n    public void send(byte[] data) {\n        super.send(data);\n        if(mGatt==null || mCWrite==null || data==null || data.length==0)\n            return;\n        /*int c = data.length/20;\n        int remain = data.length%20;\n        int count = remain==0?c:c+1;\n        for (int i=0;i<count;i++){\n            mCWrite.setValue(ByteUtils.subBytes(data, i*20, i==count-1?remain:20));\n            for (int k=0;k<4;k++){\n                if (mGatt.writeCharacteristic(mCWrite)){\n                    Logs.d(\"LPQ2\", \"成功 \"+k);\n                    try {\n                        Thread.sleep(SEND_INTERVAL);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    break;\n                }else if (k==3){\n                    Logs.d(\"LPQ2\", \"失败 \"+k);\n                    return;\n                }else {\n                    Logs.d(\"LPQ2\", \"失败 \"+k);\n                    try {\n                        Thread.sleep(SEND_INTERVAL);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }*/\n        int sendInterval = mProtocol==null? Protocol.BLE_MAX_SEND_INTERVAL :mProtocol.getMaxBleSendInterval();\n        if (data.length <= mSingleSize) {\n            mCWrite.setValue(data);\n            Logs.d(\"LPQ\", \"prepare small send\");\n            sendResult = false;\n            boolean res = mGatt.writeCharacteristic(mCWrite);\n            Logs.d(\"LPQ\", \"after small writeCharacteristic:\"+res+\" \"+ByteUtils.toString(data));\n\n            if (sendResult) {\n                Logs.d(\"LPQ\", \"small abnormal\");\n                return;\n            };\n\n            synchronized (BleHelper.this) {\n                try {\n                    long i = System.currentTimeMillis();\n                    wait(sendInterval);\n                    long t  = System.currentTimeMillis()-i;\n                    if (t>mMaxTime){\n                        mMaxTime = t;\n                        Logs.d(\"LPQ\",\"max interval：\"+mMaxTime);\n                    }\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        } else {\n            byte[] sendData = null;\n            for (int i = 0; i <= data.length / mSingleSize; i++) {\n                if (i == data.length / mSingleSize) {\n                    sendData = new byte[data.length % mSingleSize];\n                    if (sendData.length == 0)\n                        return;\n                    for (int k = 0; k < data.length % mSingleSize; k++) {\n                        sendData[k] = data[i * mSingleSize + k];\n                    }\n                } else {\n                    sendData = new byte[mSingleSize];\n                    for (int k = 0; k < mSingleSize; k++) {\n                        sendData[k] = data[i * mSingleSize + k];\n                    }\n                }\n                if (!isConnected())\n                    return;\n                mCWrite.setValue(sendData);\n                Logs.d(\"LPQ\", \"prepare big send\");\n                sendResult = false;\n                boolean res = mGatt.writeCharacteristic(mCWrite);\n                Logs.d(\"LPQ\", \"after big writeCharacteristic:\" + sendData.length + \" result:\"+res+\" \"+ByteUtils.toString(sendData));\n\n                if (sendResult) {\n                    Logs.d(\"LPQ\", \"big success\");\n                    continue;\n                };\n\n                synchronized (BleHelper.this) {\n                    try {\n                        long i2 = System.currentTimeMillis();\n                        wait(sendInterval);\n                        long t  = System.currentTimeMillis()-i2;\n                        if (t>mMaxTime){\n                            mMaxTime = t;\n                            Logs.d(\"LPQ\",\"max interval：\"+mMaxTime);\n                        }\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n    }\n\n    long mMaxTime;\n\n    private BluetoothGattCallback mCallBack = (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) ? null : new BluetoothGattCallback() {\n        @Override\n        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {\n            recv(characteristic.getValue());\n        }\n\n        @Override\n        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {\n\n        }\n\n        @Override\n        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {\n            if (newState == BluetoothProfile.STATE_CONNECTED) {\n                Logs.d(TAG, \"success，discoverServices\");\n                mGatt.discoverServices();\n            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {\n                Logs.d(TAG, \"fail，stop\");\n                stopTimeoutCount();\n                handleConnectEvent(isConnecting, false);\n            }\n            Logs.d(TAG, \"connect state---\" + newState);\n        }\n\n        @Override\n        public void onServicesDiscovered(BluetoothGatt gatt, int status) {\n            stopTimeoutCount();\n            if (status == BluetoothGatt.GATT_SUCCESS && mProtocol != null) {\n                if (mProtocol.getServiceUUID() != null) {\n                    BluetoothGattService service = mGatt.getService(mProtocol.getServiceUUID());\n                    if (service != null) {\n                        mCWrite = service.getCharacteristic(mProtocol.getSendTunnelUUID());\n                        mCRead = service.getCharacteristic(mProtocol.getRecvTunnelUUID());\n                    }else {\n                        Logs.d(\"LPQ\", \"service is null\");\n                    }\n                }\n                if (mCRead == null || mCWrite == null){\n                    for (BluetoothGattService s : mGatt.getServices()) {\n                        if(mCWrite == null){\n                            mCWrite = s.getCharacteristic(mProtocol.getSendTunnelUUID());\n                            if (mCWrite!=null)\n                                Logs.d(\"LPQ\", \"write service:\"+s.getUuid().toString());\n                        }\n                        if (mCRead == null) {\n                            mCRead = s.getCharacteristic(mProtocol.getRecvTunnelUUID());\n                            if (mCRead!=null)\n                                Logs.d(\"LPQ\", \"read service:\"+s.getUuid().toString());\n                        }\n                        if (mCWrite!=null&&mCRead!=null){\n                            break;\n                        }\n                    }\n                }\n                if (mCRead != null && mCWrite != null) {\n//                    if (mGatt.setCharacteristicNotification(mCRead, true)) {\n                        /*BluetoothGattDescriptor descriptor = mCRead.getDescriptor(mProtocol.getDescriptorUUID() == null ? DEFAULT_DESCRIPTOR_UUID : mProtocol.getDescriptorUUID());\n                        if (descriptor != null) {\n                            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);\n                            mGatt.writeDescriptor(descriptor);\n                        }*/\n\n                        /*List<BluetoothGattDescriptor> descriptors = mCRead.getDescriptors();\n                        for(BluetoothGattDescriptor dp:descriptors){\n                            dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);\n                            mGatt.writeDescriptor(dp);\n                        }*/\n//                    }\n                    boolean isSet = false;\n                    byte[] value = null;\n                    if (0 != (mCRead.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // 查看是否带有可通知属性notify\n                        value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;\n                        isSet = true;\n                    } else if (0 != (mCRead.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {  // 查看是否带有indecation属性\n                        value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;\n                        isSet = true;\n                    }\n                    if (isSet){\n                        UUID descriptorUUID = mProtocol.getDescriptorUUID() == null ? DEFAULT_DESCRIPTOR_UUID : mProtocol.getDescriptorUUID();\n                        mGatt.setCharacteristicNotification(mCRead, true);\n                        BluetoothGattDescriptor descriptor = mCRead.getDescriptor(descriptorUUID);\n                        descriptor.setValue(value);\n                        final boolean b = mGatt.writeDescriptor(descriptor);\n                        //此处必须开线程，因为onDescriptorWrite是顺序回调执行的\n                        new Thread(new Runnable() {\n                            @Override\n                            public void run() {\n                                Logs.d(TAG, \"writeDescriptor \"+b);\n                                synchronized (mGatt){\n                                    try {\n                                        mWriteDescriptorResult = false;\n                                        mGatt.wait(MAX_WRITE_DESCRIPTOR_TIME);\n                                        Logs.d(TAG, \"write --- connect success\" + isConnecting());\n                                        handleConnectEvent(isConnecting(), true);\n                                    } catch (InterruptedException e) {\n                                        e.printStackTrace();\n                                    }\n                                }\n                            }\n                        }).start();\n                    }else {\n                        Logs.d(TAG, \"no write --- connect success \" + isConnecting());\n                        handleConnectEvent(isConnecting(), true);\n                    }\n                    return;\n                } else {\n                    Logs.d(TAG, \"read-write null\");\n                }\n            } else {\n                Logs.d(TAG, \"onServicesDiscovered fail \" + status);\n            }\n            handleConnectEvent(isConnecting(), false);\n        }\n\n        @Override\n        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {\n            super.onMtuChanged(gatt, mtu, status);\n            //TODO:这个逻辑有点小问题\n            if (status == BluetoothGatt.GATT_SUCCESS) {\n                mSingleSize = mtu - 3;\n                mSetHeightSpeedModeResult = true;\n                Logs.d(\"LPQ\", \"MTU change success \"+mtu);\n            }else {\n                mSetHeightSpeedModeResult = false;\n                Logs.d(\"LPQ\", \"MTU change fail \"+mtu);\n            }\n            synchronized (mGatt){\n                mGatt.notify();\n            }\n        }\n\n        @Override\n        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {\n            super.onCharacteristicWrite(gatt, characteristic, status);\n            synchronized (BleHelper.this){\n                if (status==BluetoothGatt.GATT_SUCCESS){\n                    sendResult = true;\n                    Logs.d(\"LPQ\", \"send success \"+ByteUtils.toString(characteristic.getValue()));\n                }else {\n                    sendResult = false;\n                    Logs.d(\"LPQ\", \"sen fail，status:\"+status+\"   \"+ByteUtils.toString(characteristic.getValue()));\n                }\n                BleHelper.this.notify();\n                Logs.d(\"LPQ\", \"send complete, notify end\");\n            }\n        }\n\n        @Override\n        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {\n            super.onDescriptorWrite(gatt, descriptor, status);\n            mWriteDescriptorResult = status==BluetoothGatt.GATT_SUCCESS;\n            Logs.d(\"LPQ\", \"Write result: \"+mWriteDescriptorResult);\n            synchronized (mGatt){\n                mGatt.notify();\n            }\n        }\n    };\n\n    public boolean isHighSpeedMode(){\n        return mSingleSize > DEFAULT_SINGLE_SZIE;\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public boolean setHighConnectionPriority(boolean flag){\n        if (mGatt == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)\n            return false;\n        return mGatt.requestConnectionPriority(flag ? BluetoothGatt.CONNECTION_PRIORITY_HIGH:BluetoothGatt.CONNECTION_PRIORITY_BALANCED);\n    }\n\n    public boolean setHighSpeedMode(boolean flag){\n        mSetHeightSpeedModeResult = false;\n        if (mGatt==null)\n            return false;\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)\n            return false;\n        int mtuSize = 3;\n        if (flag){\n            mtuSize += MAX_SINGLE_SZIE;\n        }else {\n            mtuSize += DEFAULT_SINGLE_SZIE;\n        }\n\n        synchronized (mGatt){\n            boolean res = mGatt.requestMtu(mtuSize);\n            Logs.d(\"LPQ\", \"request Max Mtu \" + (mtuSize) + \" result:\"+res);\n            if (!res)\n                return false;\n\n            try {\n                mGatt.wait(300);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n        return mSetHeightSpeedModeResult;\n    }\n\n    /**\n     * 处理设备连接事件，所有BLE连接状态事件最终处理的地方\n     *\n     * @param tryConnect   是否为尝试连接事件\n     * @param connectState 连接状态，连接或断开\n     */\n    private synchronized void handleConnectEvent(boolean tryConnect, boolean connectState) {\n        if (tryConnect) {\n            setConnecting(false);\n            if (connectState) {\n                setConnected(true);\n            } else {\n                //连接失败\n                setConnected(false);\n                if (mGatt != null) {\n                    mGatt.disconnect();\n                    mGatt.close();\n                    mGatt = null;\n                }\n            }\n            sendConnectResultMessage(connectState);\n        } else {\n            //这种情况一般connectState只可能为false,即蓝牙已经成功连接后断开的情况\n            if (mGatt != null) {\n//                    mGatt.disconnect();\n                mGatt.close();\n                mGatt = null;\n            }\n            if (isConnected()) {\n                setConnected(false);\n                sendDisconnectResultMessage();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/helper/BluetoothHelper.java",
    "content": "package com.stag.bluetooth.helper;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothDevice;\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Message;\n\nimport com.stag.bluetooth.BluetoothTransfer;\nimport com.stag.bluetooth.OnBluetoothConnectStateChangeListener;\nimport com.stag.bluetooth.OnBluetoothTransmitListener;\nimport com.stag.bluetooth.protocol.Protocol;\n\n/**\n * Created by Administrator on 2016/11/14.\n */\n\npublic abstract class BluetoothHelper {\n\n    protected static final int CONNECT_TIMEOUT = 10000;    //连接超时时间\n    private static final int HANDLER_CONNECT_EVENT = 1;\n    private static final int HANDLER_DISCONNECT_EVENT = 2;\n    protected Context mContext;\n    protected boolean isConnected;\n    protected boolean isConnecting; //是否为尝试连接，用于判断连接断开的情况：连接失败或者已经连接成功后断开\n    protected BluetoothAdapter mBluetoothAdapter;\n    protected BluetoothDevice mDevice;\n    protected OnBluetoothConnectStateChangeListener listener;\n    protected OnBluetoothTransmitListener transmitListener;//蓝牙数据收发监听\n    protected Protocol mProtocol;\n    private Handler handler;\n    protected BluetoothHelper(Context context){\n        mContext = context;\n        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();\n    }\n\n    public abstract void startScan();\n\n    public abstract void stopScan();\n\n    /**\n     * BLE和传统都为改为异步进行\n     * */\n    public abstract void connect(String address);\n\n    public abstract void disconnect();\n\n    public void send(byte[] data){\n        if (transmitListener!=null)\n            transmitListener.onBluetoothSendData(data);\n    }\n\n    /**\n     * 接收数据，不可主动调用\n     * */\n    protected void recv(byte[] data){\n        if (data==null||data.length==0)\n            return;\n        if (transmitListener!=null)\n            transmitListener.onBluetoothRecvData(data);\n        BluetoothTransfer.getInstance().addRecvData(data);\n    }\n\n    protected void configHandler(){\n        handler = new Handler(){\n            @Override\n            public void handleMessage(Message msg) {\n                super.handleMessage(msg);\n                switch (msg.what){\n                    case HANDLER_CONNECT_EVENT:\n                        /*final boolean isSuccess = (Boolean) msg.obj;\n                        if (isSuccess){\n                            postDelayed(new Runnable() {\n                                @Override\n                                public void run() {\n                                    connectCallback(isSuccess);\n                                }\n                            }, 10);\n                        }else {\n                            connectCallback(isSuccess);\n                        }*/\n                        connectCallback((Boolean) msg.obj);\n                        break;\n                    case HANDLER_DISCONNECT_EVENT:\n                        disconnectCallback();\n                        break;\n                }\n            }\n        };\n    }\n\n    protected void sendConnectResultMessage(boolean isSuccess){\n        Message.obtain(handler, HANDLER_CONNECT_EVENT, isSuccess).sendToTarget();\n    }\n\n    protected void sendDisconnectResultMessage(){\n        Message.obtain(handler, HANDLER_DISCONNECT_EVENT).sendToTarget();\n    }\n\n    /**\n     * 连接回调\n     * @param isSuccess 是否成功\n     * */\n    private void connectCallback(boolean isSuccess){\n        if (listener!=null)\n            listener.onBluetoothConnect(mDevice, isSuccess);\n    }\n\n    /**\n     * 断开回调\n     * */\n    private void disconnectCallback(){\n        if (listener!=null)\n            listener.onBluetoothDisconnect(mDevice);\n    }\n\n    public boolean isConnected() {\n        return isConnected;\n    }\n\n    public void setConnected(boolean connected) {\n        isConnected = connected;\n    }\n\n    public boolean isConnecting() {\n        return isConnecting;\n    }\n\n    public void setConnecting(boolean connecting) {\n        isConnecting = connecting;\n    }\n\n    public OnBluetoothConnectStateChangeListener getConnectStateChangeListener() {\n        return listener;\n    }\n\n    public void setConnectStateChangeListener(OnBluetoothConnectStateChangeListener listener) {\n        this.listener = listener;\n    }\n\n    public OnBluetoothTransmitListener getTransmitListener() {\n        return transmitListener;\n    }\n\n    public void setTransmitListener(OnBluetoothTransmitListener transmitListener) {\n        this.transmitListener = transmitListener;\n    }\n\n    public void setProtocol(Protocol protocol){\n        mProtocol = protocol;\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/helper/MyBluetoothService.java",
    "content": "package com.stag.bluetooth.helper;\n\nimport android.bluetooth.BluetoothSocket;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.Log;\n\nimport com.stag.bluetooth.util.ByteUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\npublic class MyBluetoothService {\n    private static final String TAG = \"MyBluetoothService\";\n    private static Handler handler; // handler that gets info from Bluetooth service\n\n    // Defines several constants used when transmitting messages between the\n    // service and the UI.\n    private interface MessageConstants {\n        public static final int MESSAGE_READ = 0;\n        public static final int MESSAGE_WRITE = 1;\n        public static final int MESSAGE_TOAST = 2;\n\n        // ... (Add other message types here as needed.)\n    }\n\n    public static class ConnectedThread extends Thread {\n        private final BluetoothSocket mmSocket;\n        private final InputStream mmInStream;\n        private final OutputStream mmOutStream;\n        private byte[] mmBuffer; // mmBuffer store for the stream\n\n        public ConnectedThread(BluetoothSocket socket) {\n            mmSocket = socket;\n            InputStream tmpIn = null;\n            OutputStream tmpOut = null;\n\n            // Get the input and output streams; using temp objects because\n            // member streams are final.\n            try {\n                tmpIn = socket.getInputStream();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when creating input stream\", e);\n            }\n            try {\n                tmpOut = socket.getOutputStream();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when creating output stream\", e);\n            }\n\n            mmInStream = tmpIn;\n            mmOutStream = tmpOut;\n        }\n\n        public void run() {\n            mmBuffer = new byte[1024];\n            int numBytes; // bytes returned from read()\n\n            // Keep listening to the InputStream until an exception occurs.\n            while (true) {\n                try {\n                    // Read from the InputStream.\n                    numBytes = mmInStream.read(mmBuffer);\n                    // Send the obtained bytes to the UI activity.\n                    Log.d(TAG + \"lpq\", \"run: 收到数据: \" + numBytes);\n                    for (int i = 0; i < numBytes; i++) {\n                        int len = mmInStream.read(mmBuffer, 0, numBytes);\n                        Log.d(TAG + \"lpq\", \"run: bytes = \" + ByteUtils.toString(mmBuffer));\n                    }\n//                    Message readMsg = handler.obtainMessage(\n//                            MessageConstants.MESSAGE_READ, numBytes, -1,\n//                            mmBuffer);\n//                    readMsg.sendToTarget();\n                } catch (IOException e) {\n                    Log.d(TAG + \"lpq\", \"Input stream was disconnected\", e);\n                    break;\n                }\n            }\n        }\n\n        // Call this from the main activity to send data to the remote device.\n        public void write(byte[] bytes) {\n            try {\n                mmOutStream.write(bytes);\n\n                // Share the sent message with the UI activity.\n                Message writtenMsg = handler.obtainMessage(\n                        MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);\n                writtenMsg.sendToTarget();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when sending data\", e);\n\n                // Send a failure message back to the activity.\n                Message writeErrorMsg =\n                        handler.obtainMessage(MessageConstants.MESSAGE_TOAST);\n                Bundle bundle = new Bundle();\n                bundle.putString(\"toast\",\n                        \"Couldn't send data to the other device\");\n                writeErrorMsg.setData(bundle);\n                handler.sendMessage(writeErrorMsg);\n            }\n        }\n\n        // Call this method from the main activity to shut down the connection.\n        public void cancel() {\n            try {\n                mmSocket.close();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Could not close the connect socket\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/helper/TraditionHelper.java",
    "content": "package com.stag.bluetooth.helper;\n\nimport android.bluetooth.BluetoothSocket;\nimport android.content.Context;\n\nimport com.stag.bluetooth.util.ByteUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.UUID;\n\n/**\n * Created by Administrator on 2016/11/14.\n */\n\npublic final class TraditionHelper extends BluetoothHelper {\n\n    private final static UUID SPP_UUID = UUID.fromString(\"00001101-0000-1000-8000-00805F9B34FB\"); // SPP服务UUID号\n    private static TraditionHelper instance;\n    private BluetoothSocket bthSocket;\n    private InputStream is;\n    private OutputStream os;\n\n    public static TraditionHelper getInstance(Context context){\n        if (instance==null){\n            synchronized (TraditionHelper.class){\n                instance = new TraditionHelper(context);\n            }\n        }\n        return instance;\n    }\n\n    private TraditionHelper(Context context){\n        super(context);\n    }\n\n    @Override\n    public void startScan() {\n        mBluetoothAdapter.startDiscovery();\n    }\n\n    @Override\n    public void stopScan() {\n        mBluetoothAdapter.cancelDiscovery();\n    }\n\n    @Override\n    public void connect(final String address) {\n        setConnecting(true);\n        configHandler();\n        new Thread(){\n            @Override\n            public void run() {\n                super.run();\n                mDevice = mBluetoothAdapter.getRemoteDevice(address);\n                try {\n                    bthSocket = mDevice.createRfcommSocketToServiceRecord(SPP_UUID);\n                    bthSocket.connect();\n                    is = bthSocket.getInputStream();\n                    os = bthSocket.getOutputStream();\n                } catch (IOException e) {\n                    try {\n                        bthSocket =(BluetoothSocket) mDevice.getClass().getMethod(\"createRfcommSocket\", new Class[] {int.class}).invoke(mDevice,1);\n                        bthSocket.connect();\n                        is = bthSocket.getInputStream();\n                        os = bthSocket.getOutputStream();\n                    } catch (IllegalAccessException e1) {\n                        e1.printStackTrace();\n                        sendConnectResultMessage(false);\n                        return;\n                    } catch (InvocationTargetException e1) {\n                        e1.printStackTrace();\n                        sendConnectResultMessage(false);\n                        return;\n                    } catch (NoSuchMethodException e1) {\n                        e1.printStackTrace();\n                        sendConnectResultMessage(false);\n                        return;\n                    } catch (IOException e1) {\n                        e1.printStackTrace();\n                        sendConnectResultMessage(false);\n                        return;\n                    } finally {\n                        setConnecting(false);\n                    }\n                }\n                setConnected(true);\n                new Thread(readThread).start();\n                sendConnectResultMessage(true);\n            }\n        }.start();\n    }\n\n    @Override\n    public void disconnect() {\n        if (isConnected()){\n            setConnected(false);\n            if (bthSocket != null) {\n                try {\n                    is.close();\n                    os.close();\n                    bthSocket.close();\n                    bthSocket=null;\n                    is=null;\n                    os=null;\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            sendDisconnectResultMessage();\n        }\n    }\n\n    private Runnable readThread = new Runnable() {\n        @Override\n        public void run() {\n            byte[] buffer = new byte[1024];\n            int num;\n            while (isConnected()) {\n                try {\n//                    if (is.available() > 0) {\n                        num = is.read(buffer, 0, 1024);\n                        if (num > 0) {\n                            recv(ByteUtils.subBytes(buffer, 0, num));\n                        }\n//                    } else {\n//                        Thread.sleep(1);\n//                    }\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    };\n\n    @Override\n    public void send(byte[] data) {\n        super.send(data);\n        if (data==null || data.length==0)\n            return;\n        if (os != null) {\n            try {\n                os.write(data, 0, data.length);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/helper/TraditionServerHelper.java",
    "content": "package com.stag.bluetooth.helper;\n\nimport android.bluetooth.BluetoothAdapter;\nimport android.bluetooth.BluetoothServerSocket;\nimport android.bluetooth.BluetoothSocket;\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.stag.bluetooth.util.ByteUtils;\nimport com.stag.bluetooth.util.LogUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.UUID;\n\npublic class TraditionServerHelper extends BluetoothHelper {\n    private static final String TAG = \"TraditionServerHelp-\";\n    private static final String NAME = \"XGD N6\";\n    private final static UUID SPP_UUID = UUID.fromString(\"00001101-0000-1000-8000-00805F9B34FB\"); // SPP服务UUID号\n    private static TraditionServerHelper instance;\n\n    public static TraditionServerHelper getInstance(Context context) {\n        if (instance == null) {\n            synchronized (TraditionServerHelper.class) {\n                instance = new TraditionServerHelper(context);\n            }\n        }\n        return instance;\n    }\n\n    private TraditionServerHelper(Context context) {\n        super(context);\n        acceptThread = new AcceptThread();\n    }\n\n    @Override\n    public void startScan() {\n        LogUtils.d(TAG + \"lpq\", \"startScan: 不可调用\");\n        if (acceptThread == null) {\n            acceptThread = new AcceptThread();\n        }\n        acceptThread.start();\n        configHandler();\n    }\n\n    @Override\n    public void stopScan() {\n        LogUtils.d(TAG + \"lpq\", \"stopScan: 不可调用\");\n        if (acceptThread != null) {\n            acceptThread.cancel();\n            acceptThread = null;\n        }\n    }\n\n    @Override\n    public void connect(String address) {\n        LogUtils.d(TAG + \"lpq\", \"connect: 不可调用\");\n    }\n\n    @Override\n    public void disconnect() {\n        LogUtils.d(TAG + \"lpq\", \"disconnect: 不可调用\");\n        if (connectedThread != null) {\n\n            connectedThread.cancel();\n        }\n    }\n\n    private AcceptThread acceptThread;\n\n    private class AcceptThread extends Thread {\n        private final BluetoothServerSocket mmServerSocket;\n\n        public AcceptThread() {\n            // Use a temporary object that is later assigned to mmServerSocket\n            // because mmServerSocket is final.\n            BluetoothServerSocket tmp = null;\n            try {\n                // MY_UUID is the app's UUID string, also used by the client code.\n                tmp = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommWithServiceRecord(NAME, SPP_UUID);\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Socket's listen() method failed\", e);\n            }\n            mmServerSocket = tmp;\n        }\n\n        public void run() {\n            BluetoothSocket socket = null;\n            // Keep listening until exception occurs or a socket is returned.\n            while (true) {\n                try {\n                    setConnecting(true);\n                    socket = mmServerSocket.accept();\n                } catch (IOException e) {\n                    Log.e(TAG + \"lpq\", \"等待连接时发生异常\", e);\n                    break;\n                }\n                setConnecting(false);\n\n                if (socket != null) {\n                    // A connection was accepted. Perform work associated with\n                    // the connection in a separate thread.\n//                    manageMyConnectedSocket(socket);\n                    connectedThread = new ConnectedThread(socket);\n                    connectedThread.start();\n                    setConnected(true);\n                    sendConnectResultMessage(true);\n                    Log.d(TAG + \"lpq\", \"run: 蓝牙连接成功\");\n                    try {\n                        mmServerSocket.close();\n                        break;\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                    break;\n                }\n            }\n        }\n\n        // Closes the connect socket and causes the thread to finish.\n        public void cancel() {\n            try {\n                mmServerSocket.close();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Could not close the connect socket\", e);\n            }\n        }\n    }\n\n    private ConnectedThread connectedThread;\n\n    public class ConnectedThread extends Thread {\n        private final BluetoothSocket mmSocket;\n        private final InputStream mmInStream;\n        private final OutputStream mmOutStream;\n        private byte[] mmBuffer; // mmBuffer store for the stream\n\n        public ConnectedThread(BluetoothSocket socket) {\n            mmSocket = socket;\n            InputStream tmpIn = null;\n            OutputStream tmpOut = null;\n\n            // Get the input and output streams; using temp objects because\n            // member streams are final.\n            try {\n                tmpIn = socket.getInputStream();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when creating input stream\", e);\n            }\n            try {\n                tmpOut = socket.getOutputStream();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when creating output stream\", e);\n            }\n\n            mmInStream = tmpIn;\n            mmOutStream = tmpOut;\n        }\n\n        public void run() {\n            mmBuffer = new byte[1024];\n            int numBytes; // bytes returned from read()\n\n            // Keep listening to the InputStream until an exception occurs.\n            while (true) {\n                try {\n                    // Read from the InputStream.\n                    numBytes = mmInStream.read(mmBuffer, 0, 1024);\n                    Log.d(TAG + \"lpq\", \"run: 收到数据: \" + numBytes);\n                    if (numBytes > 0) {\n                        recv(ByteUtils.subBytes(mmBuffer, 0, numBytes));\n                    }\n//                    Message readMsg = handler.obtainMessage(\n//                            MessageConstants.MESSAGE_READ, numBytes, -1,\n//                            mmBuffer);\n//                    readMsg.sendToTarget();\n                } catch (IOException e) {\n                    Log.d(TAG + \"lpq\", \"Input stream was disconnected\", e);\n                    break;\n                }\n            }\n        }\n\n        // Call this from the main activity to send data to the remote device.\n        public void write(byte[] bytes) {\n            try {\n                mmOutStream.write(bytes);\n\n                // Share the sent message with the UI activity.\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Error occurred when sending data\", e);\n            }\n        }\n\n        // Call this method from the main activity to shut down the connection.\n        public void cancel() {\n            try {\n                mmSocket.close();\n                sendDisconnectResultMessage();\n            } catch (IOException e) {\n                Log.e(TAG + \"lpq\", \"Could not close the connect socket\", e);\n            }\n        }\n    }\n\n    @Override\n    public void send(byte[] data) {\n        super.send(data);\n        if (data == null || data.length == 0)\n            return;\n        if (connectedThread != null && connectedThread.mmOutStream != null) {\n            try {\n                connectedThread.mmOutStream.write(data, 0, data.length);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/packet/Packet.java",
    "content": "package com.stag.bluetooth.packet;\n\n/**\n * Created by Administrator on 2016/11/14.\n */\n\npublic class Packet<T extends Packet>{\n\n    protected int cmd;  //命令\n    protected byte[] data;//数据（包含的主要信息）\n//    protected int serialNumber;//流水号，暂时业务不需要\n\n    public Packet(int cmd) {\n        this(cmd, new byte[0]);\n    }\n\n    public Packet(int cmd, byte[] data) {\n        this.cmd = cmd;\n        this.data = data==null?new byte[0]:data;\n    }\n\n    /**\n     * 用来与接收包进行匹配判断\n     * @param recvPacket 接收到的字节数据处理的结果包\n     * */\n    public boolean match(T recvPacket){\n        return cmd==recvPacket.cmd;\n    }\n\n    public int getCmd() {\n        return cmd;\n    }\n\n    public void setCmd(int cmd) {\n        this.cmd = cmd;\n    }\n\n    public byte[] getData() {\n        return data;\n    }\n\n    public void setData(byte[] data) {\n        this.data = data;\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/protocol/OnEventListener.java",
    "content": "package com.stag.bluetooth.protocol;\n\n/**\n * 监听蓝牙设备的主动上传事件，不同协议各不相同\n * Created by Administrator on 2016/11/15.\n */\npublic interface OnEventListener {\n\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/protocol/ParseResult.java",
    "content": "package com.stag.bluetooth.protocol;\n\nimport com.stag.bluetooth.BluetoothDispatch;\nimport com.stag.bluetooth.packet.Packet;\n\n/**\n * 蓝牙收到的数据解析结果\n * Created by Administrator on 2016/11/15.\n */\n\npublic final class ParseResult {\n\n    private ResultType type;\n\n    private Packet packet;\n\n    private BluetoothDispatch.Callback callback;    //主动事件才不为null\n\n    public ParseResult(){\n\n    };\n\n    public ParseResult(ResultType type, Packet packet, BluetoothDispatch.Callback callback) {\n        this.type = type;\n        this.packet = packet;\n        this.callback = callback;\n    }\n\n    public ResultType getType() {\n        return type;\n    }\n\n    public void setType(ResultType type) {\n        this.type = type;\n    }\n\n    public Packet getPacket() {\n        return packet;\n    }\n\n    public void setPacket(Packet packet) {\n        this.packet = packet;\n    }\n\n    public BluetoothDispatch.Callback getCallback() {\n        return callback;\n    }\n\n    public void setCallback(BluetoothDispatch.Callback callback) {\n        this.callback = callback;\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/protocol/Protocol.java",
    "content": "package com.stag.bluetooth.protocol;\n\nimport android.content.Context;\n\nimport com.stag.bluetooth.packet.Packet;\n\nimport java.util.UUID;\n\n/**\n * Created by Administrator on 2016/11/14.\n */\n\npublic abstract class Protocol<E extends Packet, T extends OnEventListener> {\n    public final static int BLE_MAX_SEND_INTERVAL = 500;\n    protected Context mContext;\n\n    private T mEventListener;\n\n    private Object mData;\n\n    private int mMaxBleSendInterval = BLE_MAX_SEND_INTERVAL;\n\n    /**\n     * 协议所特有的主动事件监听\n     */\n    protected Protocol(Context context) {\n        this(context, null);\n    }\n\n    /**\n     * 协议所特有的主动事件监听\n     */\n    protected Protocol(Context context, T listener) {\n        mContext = context.getApplicationContext();\n        mEventListener = listener;\n    }\n\n    /**\n     * 初始化\n     * */\n    public void initialize(){\n\n    }\n\n    /**\n     * 销毁\n     * */\n    public void destroy(){\n        mContext = null;\n        mEventListener = null;\n    }\n\n    /**\n     * 发送包处理成最终要发送的字节数据\n     */\n    public abstract byte[] packetToBytes(E packet);\n\n    /**\n     * 解析收到的字节处理成结果\n     */\n    public abstract ParseResult parse(byte[] data);\n\n    /**\n     * 获取协议类型\n     * */\n    public abstract int getType();\n\n    /**\n     * 是否设置了主动事件监听\n     * */\n    protected boolean haveSetEventListener(){\n        return mEventListener!=null;\n    }\n\n    public UUID getServiceUUID() {\n        return UUID.fromString(\"0000fff0-0000-1000-8000-00805f9b34fb\");\n    }\n\n    public UUID getSendTunnelUUID() {\n        return UUID.fromString(\"0000fff6-0000-1000-8000-00805f9b34fb\");\n    }\n\n    public UUID getRecvTunnelUUID() {\n        return UUID.fromString(\"0000fff7-0000-1000-8000-00805f9b34fb\");\n    }\n\n    public UUID getDescriptorUUID() {\n        return UUID.fromString(\"00002902-0000-1000-8000-00805f9b34fb\");\n    }\n\n    /**\n     * 建议最大发送间隔\n     * */\n    public void setMaxBleSendInterval(int interval) {\n        mMaxBleSendInterval = interval;\n    }\n\n    /**\n     * 有的协议对应目标硬件接收间隔有限制，例如生久\n     * */\n    public int getMaxBleSendInterval() {\n        return mMaxBleSendInterval;\n    }\n\n    public T getEventListener() {\n        return mEventListener;\n    }\n\n    public void setEventListener(T eventListener) {\n        this.mEventListener = eventListener;\n    }\n\n    public Object getData() {\n        return mData;\n    }\n\n    public void setData(Object data) {\n        this.mData = data;\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/protocol/ResultType.java",
    "content": "package com.stag.bluetooth.protocol;\n\n/**\n * Created by Administrator on 2016/11/28.\n */\n\npublic enum ResultType {\n    RESPOND,    //响应事件\n    ACTIVE,     //主动事件\n    INCOMPLETE  //非完整一帧\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/util/ByteUtils.java",
    "content": "package com.stag.bluetooth.util;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n/**\n * Created by Administrator on 2016/7/25 0025.\n */\npublic final class ByteUtils {\n\n    /**\n     * 比较两个byte数组\n     *\n     * @param first\n     * @param second\n     * @return\n     */\n    public static boolean compareBytes(byte[] first, byte[] second) {\n        for (int i = 0; i < (first.length < second.length ? first.length : second.length); i++) {\n            if (first[i] != second[i]) {\n                return false;\n            }\n        }\n        return true && first.length == second.length;\n    }\n\n    /**\n     * 缺失部分有效数字\n     * long型转成4字节byte 大端模式\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToByte4BigEndian(long l) {\n        byte[] b = new byte[4];\n        b[3] = (byte) (l & 0xffL);\n        b[2] = (byte) ((l >> 8) & 0xff00L);\n        b[1] = (byte) ((l >> 16) & 0xff0000L);\n        b[0] = (byte) ((l >> 24) & 0xff000000L);\n        return b;\n    }\n\n    /**\n     * 4字节byte转化成Long 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static long byte4ToLongBigEndian(byte[] b) {\n        long l = (long)b[3] & 0xffL;\n        l |= ((long)b[2] << 8) & 0xff00L;\n        l |= ((long)b[1] << 16) & 0xff0000L;\n        l |= ((long)b[0] << 24) & 0xff000000L;\n        return l;\n    }\n\n    /**\n     * 16个字节转化UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte16ToUuidBigEndian(byte[] b) {\n        return new UUID(byte8ToLongBigEndian(subBytes(b, 0, 8)), byte8ToLongBigEndian(subBytes(b, 8, 8)));\n    }\n\n    /**\n     * UUID转化成16个字节 大端模式\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte16BigEndian(UUID uuid) {\n        long l = uuid.getLeastSignificantBits();\n        long l2 = uuid.getMostSignificantBits();\n        return combineByteArray(longToByte8BigEndian(l2), longToByte8BigEndian(l));\n    }\n\n    /**\n     * 8个字节转化UUID 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static UUID byte8ToUuidBigEndian(byte[] b) {\n        return new UUID(0, byte8ToLongBigEndian(b));\n    }\n\n    /**\n     * UUID转化成8个字节 大端模式\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToByte8BigEndian(UUID uuid) {\n        long l = uuid.getLeastSignificantBits();\n        return longToByte8BigEndian(l);\n    }\n\n    /**\n     * 8个字节转化成long型 大端模式\n     *\n     * @param b\n     * @return\n     */\n    public static long byte8ToLongBigEndian(byte[] b) {\n        long l = (long)b[7] & 0xffL;\n        l |= ((long)b[6] << 8) & 0xff00L;\n        l |= ((long)b[5] << 16) & 0xff0000L;\n        l |= ((long)b[4] << 24) & 0xff000000L;\n        l |= ((long)b[3] << 32) & 0xff00000000L;\n        l |= ((long)b[2] << 40) & 0xff0000000000L;\n        l |= ((long)b[1] << 48) & 0xff000000000000L;\n        l |= ((long)b[0] << 56) & 0xff00000000000000L;\n        return l;\n    }\n\n    /**\n     * long型转成8个字节 大端模式\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToByte8BigEndian(long l) {\n        byte[] b = new byte[8];\n        b[7] = (byte) (l & 0xffL);\n        b[6] = (byte) ((l >> 8) & 0xffL);\n        b[5] = (byte) ((l >> 16) & 0xffL);\n        b[4] = (byte) ((l >> 24) & 0xffL);\n        b[3] = (byte) ((l >> 32) & 0xffL);\n        b[2] = (byte) ((l >> 40) & 0xffL);\n        b[1] = (byte) ((l >> 48) & 0xffL);\n        b[0] = (byte) ((l >> 56) & 0xffL);\n        return b;\n    }\n    /**\n     * 大端模式将4个字节的Byte转成int型\n     *\n     * @param b\n     * @return\n     */\n    public static int byte4ToIntBigEndian(byte[] b) {\n        int i = b[3] & 0xff;\n        i |= ((b[2] << 8) & 0xff00);\n        i |= ((b[1] << 16) & 0xff0000);\n        i |= ((b[0] << 24) & 0xff000000);\n        return i;\n    }\n\n    /**\n     * 大端模式将int型转成4个字节的Byte\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToByte4BigEndian(int i) {\n        byte[] res = new byte[4];\n        res[3] = (byte) (i & 0xff);\n        res[2] = (byte) ((i >> 8) & 0xff);\n        res[1] = (byte) ((i >> 16) & 0xff);\n        res[0] = (byte) ((i >> 24) & 0xff);\n        return res;\n    }\n\n    /**\n     * 大端模式将3个字节的Byte转成int型\n     *\n     * @param b\n     * @return\n     */\n    public static int byte3ToIntBigEndian(byte[] b) {\n        int i = b[2] & 0xff;\n        i |= ((b[1] << 8) & 0xff00);\n        i |= ((b[1] << 16) & 0xff0000);\n        return i;\n    }\n\n    /**\n     * 大端模式将int型转成3个字节的Byte\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToByte3BigEndian(int i) {\n        byte[] res = new byte[3];\n        res[2] = (byte) (i & 0xff);\n        res[1] = (byte) ((i >> 8) & 0xff);\n        res[0] = (byte) ((i >> 16) & 0xff);\n        return res;\n    }\n\n    public static String toString(byte[] data) {\n        if (data == null) {\n            return null;\n        }\n        StringBuffer buffer = new StringBuffer();\n        for (byte b : data) {\n            String d = Integer.toHexString(b & 0xff);\n            if (d.length() > 1) {\n                buffer.append(d + \" \");\n            } else {\n                buffer.append(\"0\" + d + \" \");\n            }\n        }\n        return buffer.toString();\n    }\n\n    public static byte[] subBytes(byte[] data, int start, int len) {\n        byte[] res = new byte[len];\n        for (int i = 0; i < len; i++) {\n            res[i] = data[start + i];\n        }\n        return res;\n    }\n\n    public static byte[] subBytes(byte[] data, int start) {\n        int len = data.length - start;\n        return subBytes(data, start, len);\n    }\n\n    /**\n     * 将字节数组转化成Long\n     *\n     * @param b\n     * @return\n     */\n    public static long bytesToLong(byte[] b) {\n        long l = ((long) b[0] << 56) & 0xFF00000000000000L;\n        // 如果不强制转换为long，那么默认会当作int，导致最高32位丢失\n        l |= ((long) b[1] << 48) & 0xFF000000000000L;\n        l |= ((long) b[2] << 40) & 0xFF0000000000L;\n        l |= ((long) b[3] << 32) & 0xFF00000000L;\n        l |= ((long) b[4] << 24) & 0xFF000000L;\n        l |= ((long) b[5] << 16) & 0xFF0000L;\n        l |= ((long) b[6] << 8) & 0xFF00L;\n        l |= b[7] & 0xFFL;\n        return l;\n    }\n\n    /**\n     * 将Long转化成字节数组\n     *\n     * @param l\n     * @return\n     */\n    public static byte[] longToBytes(long l) {\n        byte[] b = new byte[8];\n        b[0] = (byte) (l >>> 56);\n        b[1] = (byte) (l >>> 48);\n        b[2] = (byte) (l >>> 40);\n        b[3] = (byte) (l >>> 32);\n        b[4] = (byte) (l >>> 24);\n        b[5] = (byte) (l >>> 16);\n        b[6] = (byte) (l >>> 8);\n        b[7] = (byte) (l);\n        return b;\n    }\n\n    /**\n     * 合并Byte数组\n     *\n     * @param bytes\n     * @return\n     */\n    public static byte[] combineByteArray(byte[]... bytes) {\n        int len = 0;\n        for (byte[] bs : bytes) {\n            if (bs != null) {\n                len += bs.length;\n            }\n        }\n        byte[] res = new byte[len];\n        int pos = 0;\n        for (byte[] bs : bytes) {\n            if (bs != null) {\n                for (byte b : bs) {\n                    res[pos++] = b;\n                }\n            }\n        }\n        return res;\n    }\n\n    /**\n     * 将UUID转化成Byte数组\n     *\n     * @param uuid\n     * @return\n     */\n    public static byte[] uuidToBytes(UUID uuid) {\n        return combineByteArray(longToBytes(uuid.getMostSignificantBits()), longToBytes(uuid.getLeastSignificantBits()));\n    }\n\n    /**\n     * 将Byte数组转化成UUID\n     *\n     * @param data\n     * @return\n     */\n    public static UUID bytesToUuid(byte[] data) {\n        if (data == null || data.length < 16)\n            return null;\n        byte[] bMost = subBytes(data, 0, 8);\n        byte[] bLeast = subBytes(data, 8, 8);\n        return new UUID(bytesToLong(bMost), bytesToLong(bLeast));\n    }\n\n    /**\n     * 将Int转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytes2(int i) {\n        byte[] res = new byte[2];\n        res[1] = (byte) (i & 0xFF);\n        res[0] = (byte) (i >> 8 & 0xFF);\n        return res;\n    }\n\n    public static byte[] intToBytes2SmallDian(int i) {\n        byte[] res = new byte[2];\n        res[0] = (byte) (i & 0xFF);\n        res[1] = (byte) (i >> 8 & 0xFF);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToIntBigEndian(byte[] b) {\n        int res = (int) ((b[0] << 24) & 0xFF000000L);\n        res |= (b[1] << 16 & 0xFF0000L);\n        res |= (b[2] << 8 & 0xFF00L);\n        res |= (b[3] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToIntSmallEndian(byte[] b) {\n        int res = (int) ((b[3] << 24) & 0xFF000000L);\n        res |= (b[2] << 16 & 0xFF0000L);\n        res |= (b[1] << 8 & 0xFF00L);\n        res |= (b[0] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Int转化成Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytesBigEndian(int i) {\n        byte[] res = new byte[4];\n        res[0] = (byte) ((i >> 24) & 0xFFL);\n        res[1] = (byte) ((i >> 16) & 0xFFL);\n        res[2] = (byte) ((i >> 8) & 0xFFL);\n        res[3] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化为int\n     *\n     * @param b\n     * @return\n     */\n    public static int bytesToInt(byte[] b) {\n        int res = (int) ((b[3] << 24) & 0xFF000000L);\n        res |= (b[2] << 16 & 0xFF0000L);\n        res |= (b[1] << 8 & 0xFF00L);\n        res |= (b[0] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Int转化成Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] intToBytes(int i) {\n        byte[] res = new byte[4];\n        res[3] = (byte) ((i >> 24) & 0xFFL);\n        res[2] = (byte) ((i >> 16) & 0xFFL);\n        res[1] = (byte) ((i >> 8) & 0xFFL);\n        res[0] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Byte数组转化成short\n     *\n     * @param b\n     * @return\n     */\n    public static short bytesToShort(byte[] b) {\n        short res = (short) ((b[1] << 8) & 0xFF00L);\n        res |= (b[0] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 将Short转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] shortToBytes(short i) {\n        byte[] res = new byte[2];\n        res[1] = (byte) ((i >> 8) & 0xFFL);\n        res[0] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 大端序\n     * 将Byte数组转化成short\n     *\n     * @param b\n     * @return\n     */\n    public static short bytesToShortBigEndian(byte[] b) {\n        short res = (short) ((b[0] << 8) & 0xFF00L);\n        res |= (b[1] & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 大端序\n     * 将Short转化为Byte数组\n     *\n     * @param i\n     * @return\n     */\n    public static byte[] shortToBytesBigEndian(short i) {\n        byte[] res = new byte[2];\n        res[0] = (byte) ((i >> 8) & 0xFFL);\n        res[1] = (byte) (i & 0xFFL);\n        return res;\n    }\n\n    /**\n     * 填充数据至长度为N的数组\n     *\n     * @param data\n     * @param len\n     * @return\n     */\n    public static byte[] wrapData(byte[] data, int len) {\n        byte[] res = new byte[len];\n        for (int i = 0; i < len; i++) {\n            if (i < data.length) {\n                res[i] = data[i];\n            } else {\n                res[i] = 0;\n            }\n        }\n        return res;\n    }\n\n    /**\n     * 取出一字节中每一位的数值\n     *\n     * @param data\n     * @return\n     */\n    public static byte[] getByteBits(byte data) {\n        byte[] result = new byte[8];\n        for (int i = 0; i < 8; i++) {\n            result[i] = (byte) (data >> i & 0x01);\n        }\n        return result;\n    }\n\n    public static byte[] getBytesWithBuffer(ByteBuffer buffer) {\n        if (buffer == null)\n            return null;\n        byte[] bytes = new byte[buffer.position()];\n        for (int i = 0; i < buffer.position(); i++)\n            bytes[i] = buffer.array()[i];\n        return bytes;\n    }\n\n    /**\n     * 获取bytes的Ascii码对应的字符串\n     * @param bytes\n     * @return\n     */\n    public static String getAsciiString(byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        }\n        String result = \"\";\n        for (int i = 0; i < bytes.length; ++i) {\n            result += (char)bytes[i];\n        }\n        return result;\n    }\n\n\n    public static String bytesToHexString(byte[] src){\n        StringBuilder stringBuilder = new StringBuilder(\"\");\n        if (src == null || src.length <= 0) {\n            return null;\n        }\n        for (int i = 0; i < src.length; i++) {\n            int v = src[i] & 0xFF;\n            String hv = Integer.toHexString(v);\n            if (hv.length() < 2) {\n                stringBuilder.append(0);\n            }\n            stringBuilder.append(hv);\n        }\n        return stringBuilder.toString();\n    }\n\n\n\n    /**\n     * byte[] To list\n     * @param data\n     * @return\n     */\n    public static List<Byte> byteToList(byte[] data) {\n        List<Byte> list = null;\n        if (data != null && data.length > 0) {\n            list = new ArrayList<>();\n            for (int i = 0; i < data.length; ++i) {\n                list.add(data[i]);\n            }\n        }\n        return list;\n    }\n\n\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/util/LogUtils.java",
    "content": "package com.stag.bluetooth.util;\n\nimport android.util.Log;\n\nimport com.stag.bluetooth.BuildConfig;\n\n\npublic class LogUtils {\n\n    private static boolean isDebug = true;\n\n    private LogUtils() {\n    }\n\n    public static void v(String tag, String msg) {\n        if (isDebug) {\n            Log.v(tag, msg);\n        }\n    }\n\n    public static void d(String tag, String msg) {\n        if (isDebug) {\n            //信息太长,分段打印\n            //因为String的length是字符数量不是字节数量所以为了防止中文字符过多，\n            //  把4*1024的MAX字节打印长度改为2001字符数\n            int max_str_length = 2001 - tag.length();\n            //大于4000时\n            while (msg.length() > max_str_length) {\n                Log.d(tag, msg.substring(0, max_str_length));\n                msg = msg.substring(max_str_length);\n            }\n            //剩余部分\n            Log.d(tag, msg);\n        }\n    }\n\n    public static void i(String tag, String msg) {\n        if (isDebug) {\n            Log.i(tag, msg);\n        }\n    }\n\n    public static void w(String tag, String msg) {\n        if (isDebug) {\n            Log.w(tag, msg);\n        }\n    }\n\n    public static void e(String tag, String msg) {\n        if (isDebug) {\n            Log.e(tag, msg);\n        }\n    }\n\n}\n"
  },
  {
    "path": "bluetooth/src/main/java/com/stag/bluetooth/util/Logs.java",
    "content": "package com.stag.bluetooth.util;\n\nimport android.util.Log;\n\n/**\n * Created by Administrator on 2017/6/21.\n */\n\npublic class Logs {\n\n    private static final boolean DEBUG = true;\n\n    public static void d(String tag, String msg){\n        if (DEBUG)\n            Log.d(tag, msg);\n    }\n\n    public static void e(String tag, String msg){\n        if (DEBUG)\n            Log.e(tag, msg);\n    }\n}\n"
  },
  {
    "path": "bluetooth/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Bluetooth</string>\n</resources>\n"
  },
  {
    "path": "bluetooth/src/test/java/com/stag/bluetooth/ExampleUnitTest.java",
    "content": "package com.stag.bluetooth;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n    repositories {\n        google()\n        jcenter()\n        \n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.6.3'\n        classpath 'com.tencent.bugly:symtabfileuploader:latest.release'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n        \n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sat Apr 18 17:08:04 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Automatically convert third-party libraries to use AndroidX\nandroid.enableJetifier=true\n\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name='Bluetooth'\ninclude ':app', ':bluetooth'\n"
  }
]