liuxiulin 1 mesiac pred
commit
f3cb89fcf6
100 zmenil súbory, kde vykonal 8130 pridanie a 0 odobranie
  1. 7 0
      .gitignore
  2. 22 0
      CHANGE_LOG.md
  3. 31 0
      FastBleLib/build.gradle
  4. 17 0
      FastBleLib/proguard-rules.pro
  5. 37 0
      FastBleLib/src/main/AndroidManifest.xml
  6. 937 0
      FastBleLib/src/main/java/com/clj/fastble/BleManager.java
  7. 612 0
      FastBleLib/src/main/java/com/clj/fastble/bluetooth/BleBluetooth.java
  8. 608 0
      FastBleLib/src/main/java/com/clj/fastble/bluetooth/BleConnector.java
  9. 139 0
      FastBleLib/src/main/java/com/clj/fastble/bluetooth/MultipleBluetoothController.java
  10. 158 0
      FastBleLib/src/main/java/com/clj/fastble/bluetooth/SplitWriter.java
  11. 27 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleBaseCallback.java
  12. 24 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleGattCallback.java
  13. 13 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleIndicateCallback.java
  14. 12 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleMtuChangedCallback.java
  15. 14 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleNotifyCallback.java
  16. 12 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleReadCallback.java
  17. 12 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleRssiCallback.java
  18. 13 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleScanAndConnectCallback.java
  19. 14 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleScanCallback.java
  20. 11 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleScanPresenterImp.java
  21. 12 0
      FastBleLib/src/main/java/com/clj/fastble/callback/BleWriteCallback.java
  22. 29 0
      FastBleLib/src/main/java/com/clj/fastble/data/BleConnectStateParameter.java
  23. 112 0
      FastBleLib/src/main/java/com/clj/fastble/data/BleDevice.java
  24. 60 0
      FastBleLib/src/main/java/com/clj/fastble/data/BleMsg.java
  25. 18 0
      FastBleLib/src/main/java/com/clj/fastble/data/BleScanState.java
  26. 8 0
      FastBleLib/src/main/java/com/clj/fastble/data/BleWriteState.java
  27. 47 0
      FastBleLib/src/main/java/com/clj/fastble/exception/BleException.java
  28. 42 0
      FastBleLib/src/main/java/com/clj/fastble/exception/ConnectException.java
  29. 28 0
      FastBleLib/src/main/java/com/clj/fastble/exception/GattException.java
  30. 10 0
      FastBleLib/src/main/java/com/clj/fastble/exception/OtherException.java
  31. 10 0
      FastBleLib/src/main/java/com/clj/fastble/exception/TimeoutException.java
  32. 238 0
      FastBleLib/src/main/java/com/clj/fastble/scan/BleScanPresenter.java
  33. 109 0
      FastBleLib/src/main/java/com/clj/fastble/scan/BleScanRuleConfig.java
  34. 139 0
      FastBleLib/src/main/java/com/clj/fastble/scan/BleScanner.java
  35. 31 0
      FastBleLib/src/main/java/com/clj/fastble/utils/BleLog.java
  36. 34 0
      FastBleLib/src/main/java/com/clj/fastble/utils/BleLruHashMap.java
  37. 121 0
      FastBleLib/src/main/java/com/clj/fastble/utils/HexUtil.java
  38. 200 0
      README.md
  39. 1 0
      _config.yml
  40. 1 0
      app/.gitignore
  41. 0 0
      app/agconnect-services.json
  42. 165 0
      app/build.gradle
  43. 17 0
      app/proguard-rules.pro
  44. 20 0
      app/release/output.json
  45. 195 0
      app/src/main/AndroidManifest.xml
  46. 26 0
      app/src/main/assets/index.html
  47. 92 0
      app/src/main/java/com/rdiot/yx485/BaseActivity.java
  48. 142 0
      app/src/main/java/com/rdiot/yx485/CheckUpdateActivity.java
  49. 194 0
      app/src/main/java/com/rdiot/yx485/HttpDemoActivity.java
  50. 78 0
      app/src/main/java/com/rdiot/yx485/InviteActivity.java
  51. 49 0
      app/src/main/java/com/rdiot/yx485/MainActivity.java
  52. 245 0
      app/src/main/java/com/rdiot/yx485/PerInfoActivity.java
  53. 39 0
      app/src/main/java/com/rdiot/yx485/PermissionAndActivityResultActivity.java
  54. 57 0
      app/src/main/java/com/rdiot/yx485/QRCodeActivity.kt
  55. 62 0
      app/src/main/java/com/rdiot/yx485/UpdateBean.java
  56. 100 0
      app/src/main/java/com/rdiot/yx485/UpdateMgr.java
  57. 12 0
      app/src/main/java/com/rdiot/yx485/UpdateRequestBean.java
  58. 95 0
      app/src/main/java/com/rdiot/yx485/adapter/ContentAdapter.java
  59. 57 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceIconListAdapter.kt
  60. 89 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceListAdapter.java
  61. 86 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceRoomAdapter.java
  62. 49 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceSpinnerAdapter.java
  63. 80 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceTypeContentAdapter.kt
  64. 86 0
      app/src/main/java/com/rdiot/yx485/adapter/DeviceTypeListAdapter.java
  65. 119 0
      app/src/main/java/com/rdiot/yx485/adapter/ExampleListAdapter.java
  66. 79 0
      app/src/main/java/com/rdiot/yx485/adapter/HomeAddRoomtAdapter.java
  67. 68 0
      app/src/main/java/com/rdiot/yx485/adapter/HomeListAdapter.java
  68. 32 0
      app/src/main/java/com/rdiot/yx485/adapter/HomePagerAdapter.java
  69. 101 0
      app/src/main/java/com/rdiot/yx485/adapter/HomePersonAdapter.java
  70. 86 0
      app/src/main/java/com/rdiot/yx485/adapter/HotListAdapter.java
  71. 33 0
      app/src/main/java/com/rdiot/yx485/adapter/MainHomePagerAdapter.java
  72. 97 0
      app/src/main/java/com/rdiot/yx485/adapter/MessageListAdapter.java
  73. 71 0
      app/src/main/java/com/rdiot/yx485/adapter/RoomAdapter.java
  74. 80 0
      app/src/main/java/com/rdiot/yx485/adapter/RoomDeteleDeviceAdapter.java
  75. 80 0
      app/src/main/java/com/rdiot/yx485/adapter/RoomDeviceAdapter.java
  76. 80 0
      app/src/main/java/com/rdiot/yx485/adapter/RoomEditAdapter.java
  77. 49 0
      app/src/main/java/com/rdiot/yx485/adapter/RoomSpinnerAdapter.java
  78. 105 0
      app/src/main/java/com/rdiot/yx485/adapter/TopDeviceAdapter.java
  79. 101 0
      app/src/main/java/com/rdiot/yx485/adapter/TopDevicegridAdapter.java
  80. 94 0
      app/src/main/java/com/rdiot/yx485/adapter/TopModelAdapter.java
  81. 56 0
      app/src/main/java/com/rdiot/yx485/app/App.java
  82. 30 0
      app/src/main/java/com/rdiot/yx485/base/BaseFragment.kt
  83. 140 0
      app/src/main/java/com/rdiot/yx485/base/BaseFragmentActivity.java
  84. 18 0
      app/src/main/java/com/rdiot/yx485/base/BaseViewModel.kt
  85. 21 0
      app/src/main/java/com/rdiot/yx485/bean/BannerListBean.java
  86. 8 0
      app/src/main/java/com/rdiot/yx485/bean/CityBean.java
  87. 32 0
      app/src/main/java/com/rdiot/yx485/bean/ConsumableBean.kt
  88. 47 0
      app/src/main/java/com/rdiot/yx485/bean/CreatRoomBean.java
  89. 45 0
      app/src/main/java/com/rdiot/yx485/bean/CreateHomeBean.java
  90. 30 0
      app/src/main/java/com/rdiot/yx485/bean/DefHomeRoomBean.java
  91. 13 0
      app/src/main/java/com/rdiot/yx485/bean/DeteleRoomBean.java
  92. 74 0
      app/src/main/java/com/rdiot/yx485/bean/DeviceRoomListBean.java
  93. 18 0
      app/src/main/java/com/rdiot/yx485/bean/DeviceStatus.kt
  94. 165 0
      app/src/main/java/com/rdiot/yx485/bean/DeviceTreeBean.java
  95. 7 0
      app/src/main/java/com/rdiot/yx485/bean/DeviceidBean.java
  96. 24 0
      app/src/main/java/com/rdiot/yx485/bean/EditDeviceReply.java
  97. 10 0
      app/src/main/java/com/rdiot/yx485/bean/EditDeviceReplyBean.java
  98. 42 0
      app/src/main/java/com/rdiot/yx485/bean/GoodListBean.java
  99. 61 0
      app/src/main/java/com/rdiot/yx485/bean/HomeBase.java
  100. 9 0
      app/src/main/java/com/rdiot/yx485/bean/HomeDefBean.java

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+*.iml
+.gradle
+.idea/
+/local.properties
+.DS_Store
+/build
+/captures

+ 22 - 0
CHANGE_LOG.md

@@ -0,0 +1,22 @@
+## 2.1.0
+
+1. 修复若干bug;
+2. 移除GrennDao依赖,改为compileOnly,需要时自己在gradle中引入, implementation 'org.greenrobot:greendao:3.2.0';
+3. ZResponse中移除.class参数,不需要再传入类的.class类型信息;
+4. [HuTool](https://github.com/looly/hutool/)是java web一个非常强大的工具类库,
+但是直接迁移到android却有些问题,比如java8支持、代码量较大、较多的类用不到等。从HuTool的库中搬运了如下工具类
+(优化其内部耦合度较高,删减了一般分方法):
+   * ArrayUtil(完善,数组工具类)
+   * Assert(新增,断言工具类)
+   * CharUtil(新增,字符工具类)
+   * CompareUtil(新增,对比工具类)
+   * ExceptionUtil(新增,异常工具类)
+   * MapUtil(新增,Map工具类)
+   * NumberUtil(新增,数字工具类)
+   * ObjectUtil(新增,对象工具类,包括判空、克隆、序列化等操作)
+   * PageUtil(新增,分页工具类)
+   * RegexUtil(完善,正则工具类)
+   * StringBuilderUtil(新增,可复用的字符串生成器)
+   * StringSpliter(新增,字符串切分器)
+   * StringUtil(完善,字符串工具类)
+

+ 31 - 0
FastBleLib/build.gradle

@@ -0,0 +1,31 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+    lintOptions {
+        checkReleaseBuilds false
+        abortOnError false
+    }
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+//task makeAAR(type: Copy) {
+//    from('build/outputs/aar/')
+//    into('build/aarFloder/')
+//    include('FastBleLib-release.aar')
+//    rename ('FastBleLib-release.aar', 'FastBLE-2.4.0.aar' )
+//}
+//makeAAR.dependsOn(build)

+ 17 - 0
FastBleLib/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\development\AppData\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 37 - 0
FastBleLib/src/main/AndroidManifest.xml

@@ -0,0 +1,37 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.clj.fastble"
+    xmlns:tools="http://schemas.android.com/tools">
+
+
+    <!-- Android 12以下配置的三个蓝牙权限 -->
+    <!-- 这个权限允许程序连接到已配对的蓝牙设备, 请求连接/接收连接/传输数据需要改权限, 主要用于对配对后进行操作 -->
+    <uses-permission
+        android:name="android.permission.BLUETOOTH"
+        android:maxSdkVersion="30" />
+
+    <!-- 这个权限允许程序发现和配对蓝牙设备, 该权限用来管理蓝牙设备, 有了这个权限, 应用才能使用本机的蓝牙设备, 主要用于对配对前的操作 -->
+    <uses-permission
+        android:name="android.permission.BLUETOOTH_ADMIN"
+        android:maxSdkVersion="30" />
+
+    <!-- Android 6.0以后,12.0以下,这两个权限是必须的,蓝牙扫描周围的设备需要获取模糊的位置信息。
+   这两个权限属于同一组危险权限,在清单文件中声明之后,还需要再运行时动态获取。 -->
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <!-- Android 12以下配置的两个蓝牙权限 -->
+    <!-- Android 12 及以上版本配置的三个权限 -->
+    <!-- 您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备)-->
+    <uses-permission
+        android:name="android.permission.BLUETOOTH_SCAN"
+        tools:targetApi="s" />
+
+    <!-- 应用程序使手机蓝牙可被其它蓝牙设备发现时才需要-->
+    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
+
+    <!-- 仅应用程序与已配对的蓝牙设备通信时才需要 -->
+    <uses-permission
+        android:name="android.permission.BLUETOOTH_CONNECT"
+        tools:targetApi="s" />
+    <!-- Android 12 及以上版本配置的三个权限 -->
+
+</manifest>

+ 937 - 0
FastBleLib/src/main/java/com/clj/fastble/BleManager.java

@@ -0,0 +1,937 @@
+package com.clj.fastble;
+
+import android.annotation.TargetApi;
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Looper;
+
+import com.clj.fastble.bluetooth.BleBluetooth;
+import com.clj.fastble.bluetooth.MultipleBluetoothController;
+import com.clj.fastble.bluetooth.SplitWriter;
+import com.clj.fastble.callback.BleGattCallback;
+import com.clj.fastble.callback.BleIndicateCallback;
+import com.clj.fastble.callback.BleMtuChangedCallback;
+import com.clj.fastble.callback.BleNotifyCallback;
+import com.clj.fastble.callback.BleReadCallback;
+import com.clj.fastble.callback.BleRssiCallback;
+import com.clj.fastble.callback.BleScanAndConnectCallback;
+import com.clj.fastble.callback.BleScanCallback;
+import com.clj.fastble.callback.BleWriteCallback;
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.data.BleScanState;
+import com.clj.fastble.exception.OtherException;
+import com.clj.fastble.scan.BleScanRuleConfig;
+import com.clj.fastble.scan.BleScanner;
+import com.clj.fastble.utils.BleLog;
+
+import java.util.List;
+import java.util.UUID;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class BleManager {
+
+    private Application context;
+    private BleScanRuleConfig bleScanRuleConfig;
+    private BluetoothAdapter bluetoothAdapter;
+    private MultipleBluetoothController multipleBluetoothController;
+    private BluetoothManager bluetoothManager;
+
+    public static final int DEFAULT_SCAN_TIME = 10000;
+    private static final int DEFAULT_MAX_MULTIPLE_DEVICE = 7;
+    private static final int DEFAULT_OPERATE_TIME = 5000;
+    private static final int DEFAULT_CONNECT_RETRY_COUNT = 0;
+    private static final int DEFAULT_CONNECT_RETRY_INTERVAL = 5000;
+    private static final int DEFAULT_MTU = 23;
+    private static final int DEFAULT_MAX_MTU = 512;
+    private static final int DEFAULT_WRITE_DATA_SPLIT_COUNT = 20;
+    private static final int DEFAULT_CONNECT_OVER_TIME = 10000;
+
+    private int maxConnectCount = DEFAULT_MAX_MULTIPLE_DEVICE;
+    private int operateTimeout = DEFAULT_OPERATE_TIME;
+    private int reConnectCount = DEFAULT_CONNECT_RETRY_COUNT;
+    private long reConnectInterval = DEFAULT_CONNECT_RETRY_INTERVAL;
+    private int splitWriteNum = DEFAULT_WRITE_DATA_SPLIT_COUNT;
+    private long connectOverTime = DEFAULT_CONNECT_OVER_TIME;
+
+    public static BleManager getInstance() {
+        return BleManagerHolder.sBleManager;
+    }
+
+    private static class BleManagerHolder {
+        private static final BleManager sBleManager = new BleManager();
+    }
+
+    public void init(Application app) {
+        if (context == null && app != null) {
+            context = app;
+            if (isSupportBle()) {
+                bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
+            }
+            bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+            multipleBluetoothController = new MultipleBluetoothController();
+            bleScanRuleConfig = new BleScanRuleConfig();
+        }
+    }
+
+    /**
+     * Get the Context
+     *
+     * @return
+     */
+    public Context getContext() {
+        return context;
+    }
+
+    /**
+     * Get the BluetoothManager
+     *
+     * @return
+     */
+    public BluetoothManager getBluetoothManager() {
+        return bluetoothManager;
+    }
+
+    /**
+     * Get the BluetoothAdapter
+     *
+     * @return
+     */
+    public BluetoothAdapter getBluetoothAdapter() {
+        return bluetoothAdapter;
+    }
+
+    /**
+     * get the ScanRuleConfig
+     *
+     * @return
+     */
+    public BleScanRuleConfig getScanRuleConfig() {
+        return bleScanRuleConfig;
+    }
+
+    /**
+     * Get the multiple Bluetooth Controller
+     *
+     * @return
+     */
+    public MultipleBluetoothController getMultipleBluetoothController() {
+        return multipleBluetoothController;
+    }
+
+    /**
+     * Configure scan and connection properties
+     *
+     * @param config
+     */
+    public void initScanRule(BleScanRuleConfig config) {
+        this.bleScanRuleConfig = config;
+    }
+
+    /**
+     * Get the maximum number of connections
+     *
+     * @return
+     */
+    public int getMaxConnectCount() {
+        return maxConnectCount;
+    }
+
+    /**
+     * Set the maximum number of connections
+     *
+     * @param count
+     * @return BleManager
+     */
+    public BleManager setMaxConnectCount(int count) {
+        if (count > DEFAULT_MAX_MULTIPLE_DEVICE)
+            count = DEFAULT_MAX_MULTIPLE_DEVICE;
+        this.maxConnectCount = count;
+        return this;
+    }
+
+    /**
+     * Get operate timeout
+     *
+     * @return
+     */
+    public int getOperateTimeout() {
+        return operateTimeout;
+    }
+
+    /**
+     * Set operate timeout
+     *
+     * @param count
+     * @return BleManager
+     */
+    public BleManager setOperateTimeout(int count) {
+        this.operateTimeout = count;
+        return this;
+    }
+
+    /**
+     * Get connect retry count
+     *
+     * @return
+     */
+    public int getReConnectCount() {
+        return reConnectCount;
+    }
+
+    /**
+     * Get connect retry interval
+     *
+     * @return
+     */
+    public long getReConnectInterval() {
+        return reConnectInterval;
+    }
+
+    /**
+     * Set connect retry count and interval
+     *
+     * @param count
+     * @return BleManager
+     */
+    public BleManager setReConnectCount(int count) {
+        return setReConnectCount(count, DEFAULT_CONNECT_RETRY_INTERVAL);
+    }
+
+    /**
+     * Set connect retry count and interval
+     *
+     * @param count
+     * @return BleManager
+     */
+    public BleManager setReConnectCount(int count, long interval) {
+        if (count > 10)
+            count = 10;
+        if (interval < 0)
+            interval = 0;
+        this.reConnectCount = count;
+        this.reConnectInterval = interval;
+        return this;
+    }
+
+
+    /**
+     * Get operate split Write Num
+     *
+     * @return
+     */
+    public int getSplitWriteNum() {
+        return splitWriteNum;
+    }
+
+    /**
+     * Set split Writ eNum
+     *
+     * @param num
+     * @return BleManager
+     */
+    public BleManager setSplitWriteNum(int num) {
+        if (num > 0) {
+            this.splitWriteNum = num;
+        }
+        return this;
+    }
+
+    /**
+     * Get operate connect Over Time
+     *
+     * @return
+     */
+    public long getConnectOverTime() {
+        return connectOverTime;
+    }
+
+    /**
+     * Set connect Over Time
+     *
+     * @param time
+     * @return BleManager
+     */
+    public BleManager setConnectOverTime(long time) {
+        if (time <= 0) {
+            time = 100;
+        }
+        this.connectOverTime = time;
+        return this;
+    }
+
+    /**
+     * print log?
+     *
+     * @param enable
+     * @return BleManager
+     */
+    public BleManager enableLog(boolean enable) {
+        BleLog.isPrint = enable;
+        return this;
+    }
+
+    /**
+     * scan device around
+     *
+     * @param callback
+     */
+    public void scan(BleScanCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleScanCallback can not be Null!");
+        }
+
+        if (!isBlueEnable()) {
+            BleLog.e("Bluetooth not enable!");
+            callback.onScanStarted(false);
+            return;
+        }
+
+        UUID[] serviceUuids = bleScanRuleConfig.getServiceUuids();
+        String[] deviceNames = bleScanRuleConfig.getDeviceNames();
+        String deviceMac = bleScanRuleConfig.getDeviceMac();
+        boolean fuzzy = bleScanRuleConfig.isFuzzy();
+        long timeOut = bleScanRuleConfig.getScanTimeOut();
+
+        BleScanner.getInstance().scan(serviceUuids, deviceNames, deviceMac, fuzzy, timeOut, callback, bleScanRuleConfig.isCanRepeatFound());
+    }
+
+    /**
+     * scan device then connect
+     *
+     * @param callback
+     */
+    public void scanAndConnect(BleScanAndConnectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleScanAndConnectCallback can not be Null!");
+        }
+
+        if (!isBlueEnable()) {
+            BleLog.e("Bluetooth not enable!");
+            callback.onScanStarted(false);
+            return;
+        }
+
+        UUID[] serviceUuids = bleScanRuleConfig.getServiceUuids();
+        String[] deviceNames = bleScanRuleConfig.getDeviceNames();
+        String deviceMac = bleScanRuleConfig.getDeviceMac();
+        boolean fuzzy = bleScanRuleConfig.isFuzzy();
+        long timeOut = bleScanRuleConfig.getScanTimeOut();
+
+        BleScanner.getInstance().scanAndConnect(serviceUuids, deviceNames, deviceMac, fuzzy, timeOut, callback);
+    }
+
+    /**
+     * connect a known device
+     *
+     * @param bleDevice
+     * @param bleGattCallback
+     * @return
+     */
+    public BluetoothGatt connect(BleDevice bleDevice, BleGattCallback bleGattCallback) {
+        if (bleGattCallback == null) {
+            throw new IllegalArgumentException("BleGattCallback can not be Null!");
+        }
+
+        if (!isBlueEnable()) {
+            BleLog.e("Bluetooth not enable!");
+            bleGattCallback.onConnectFail(bleDevice, new OtherException("Bluetooth not enable!"));
+            return null;
+        }
+
+        if (Looper.myLooper() == null || Looper.myLooper() != Looper.getMainLooper()) {
+            BleLog.w("Be careful: currentThread is not MainThread!");
+        }
+
+        if (bleDevice == null || bleDevice.getDevice() == null) {
+            bleGattCallback.onConnectFail(bleDevice, new OtherException("Not Found Device Exception Occurred!"));
+        } else {
+            BleBluetooth bleBluetooth = multipleBluetoothController.buildConnectingBle(bleDevice);
+            boolean autoConnect = bleScanRuleConfig.isAutoConnect();
+            return bleBluetooth.connect(bleDevice, autoConnect, bleGattCallback);
+        }
+
+        return null;
+    }
+
+    /**
+     * connect a device through its mac without scan,whether or not it has been connected
+     *
+     * @param mac
+     * @param bleGattCallback
+     * @return
+     */
+    public BluetoothGatt connect(String mac, BleGattCallback bleGattCallback) {
+        BluetoothDevice bluetoothDevice = getBluetoothAdapter().getRemoteDevice(mac);
+        BleDevice bleDevice = new BleDevice(bluetoothDevice, 0, null, 0);
+        return connect(bleDevice, bleGattCallback);
+    }
+
+
+    /**
+     * Cancel scan
+     */
+    public void cancelScan() {
+        BleScanner.getInstance().stopLeScan();
+    }
+
+    /**
+     * notify
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_notify
+     * @param callback
+     */
+    public void notify(BleDevice bleDevice,
+                       String uuid_service,
+                       String uuid_notify,
+                       BleNotifyCallback callback) {
+        notify(bleDevice, uuid_service, uuid_notify, false, callback);
+    }
+
+    /**
+     * notify
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_notify
+     * @param useCharacteristicDescriptor
+     * @param callback
+     */
+    public void notify(BleDevice bleDevice,
+                       String uuid_service,
+                       String uuid_notify,
+                       boolean useCharacteristicDescriptor,
+                       BleNotifyCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleNotifyCallback can not be Null!");
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onNotifyFailure(new OtherException("This device not connect!"));
+        } else {
+            bleBluetooth.newBleConnector()
+                    .withUUIDString(uuid_service, uuid_notify)
+                    .enableCharacteristicNotify(callback, uuid_notify, useCharacteristicDescriptor);
+        }
+    }
+
+    /**
+     * indicate
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_indicate
+     * @param callback
+     */
+    public void indicate(BleDevice bleDevice,
+                         String uuid_service,
+                         String uuid_indicate,
+                         BleIndicateCallback callback) {
+        indicate(bleDevice, uuid_service, uuid_indicate, false, callback);
+    }
+
+    /**
+     * indicate
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_indicate
+     * @param useCharacteristicDescriptor
+     * @param callback
+     */
+    public void indicate(BleDevice bleDevice,
+                         String uuid_service,
+                         String uuid_indicate,
+                         boolean useCharacteristicDescriptor,
+                         BleIndicateCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleIndicateCallback can not be Null!");
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onIndicateFailure(new OtherException("This device not connect!"));
+        } else {
+            bleBluetooth.newBleConnector()
+                    .withUUIDString(uuid_service, uuid_indicate)
+                    .enableCharacteristicIndicate(callback, uuid_indicate, useCharacteristicDescriptor);
+        }
+    }
+
+    /**
+     * stop notify, remove callback
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_notify
+     * @return
+     */
+    public boolean stopNotify(BleDevice bleDevice,
+                              String uuid_service,
+                              String uuid_notify) {
+        return stopNotify(bleDevice, uuid_service, uuid_notify, false);
+    }
+
+    /**
+     * stop notify, remove callback
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_notify
+     * @param useCharacteristicDescriptor
+     * @return
+     */
+    public boolean stopNotify(BleDevice bleDevice,
+                              String uuid_service,
+                              String uuid_notify,
+                              boolean useCharacteristicDescriptor) {
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            return false;
+        }
+        boolean success = bleBluetooth.newBleConnector()
+                .withUUIDString(uuid_service, uuid_notify)
+                .disableCharacteristicNotify(useCharacteristicDescriptor);
+        if (success) {
+            bleBluetooth.removeNotifyCallback(uuid_notify);
+        }
+        return success;
+    }
+
+    /**
+     * stop indicate, remove callback
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_indicate
+     * @return
+     */
+    public boolean stopIndicate(BleDevice bleDevice,
+                                String uuid_service,
+                                String uuid_indicate) {
+        return stopIndicate(bleDevice, uuid_service, uuid_indicate, false);
+    }
+
+    /**
+     * stop indicate, remove callback
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_indicate
+     * @param useCharacteristicDescriptor
+     * @return
+     */
+    public boolean stopIndicate(BleDevice bleDevice,
+                                String uuid_service,
+                                String uuid_indicate,
+                                boolean useCharacteristicDescriptor) {
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            return false;
+        }
+        boolean success = bleBluetooth.newBleConnector()
+                .withUUIDString(uuid_service, uuid_indicate)
+                .disableCharacteristicIndicate(useCharacteristicDescriptor);
+        if (success) {
+            bleBluetooth.removeIndicateCallback(uuid_indicate);
+        }
+        return success;
+    }
+
+    /**
+     * write
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_write
+     * @param data
+     * @param callback
+     */
+    public void writeData(BleDevice bleDevice,
+                      String uuid_service,
+                      String uuid_write,
+                      byte[] data,
+                      boolean split,
+                      BleWriteCallback callback) {
+        write(bleDevice, uuid_service, uuid_write, data, split, callback);
+    }
+
+    /**
+     * write
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_write
+     * @param data
+     * @param split
+     * @param callback
+     */
+    public void write(BleDevice bleDevice,
+                      String uuid_service,
+                      String uuid_write,
+                      byte[] data,
+                      boolean split,
+                      BleWriteCallback callback) {
+
+        write(bleDevice, uuid_service, uuid_write, data, split, true, 0, callback);
+    }
+
+    /**
+     * write
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_write
+     * @param data
+     * @param split
+     * @param sendNextWhenLastSuccess
+     * @param intervalBetweenTwoPackage
+     * @param callback
+     */
+    public void write(BleDevice bleDevice,
+                      String uuid_service,
+                      String uuid_write,
+                      byte[] data,
+                      boolean split,
+                      boolean sendNextWhenLastSuccess,
+                      long intervalBetweenTwoPackage,
+                      BleWriteCallback callback) {
+
+        if (callback == null) {
+            throw new IllegalArgumentException("BleWriteCallback can not be Null!");
+        }
+
+        if (data == null) {
+            BleLog.e("data is Null!");
+            callback.onWriteFailure(new OtherException("data is Null!"));
+            return;
+        }
+
+        if (data.length > 20 && !split) {
+            BleLog.w("Be careful: data's length beyond 20! Ensure MTU higher than 23, or use spilt write!");
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onWriteFailure(new OtherException("This device not connect!"));
+        } else {
+            if (split && data.length > getSplitWriteNum()) {
+                new SplitWriter().splitWrite(bleBluetooth, uuid_service, uuid_write, data,
+                        sendNextWhenLastSuccess, intervalBetweenTwoPackage, callback);
+            } else {
+                bleBluetooth.newBleConnector()
+                        .withUUIDString(uuid_service, uuid_write)
+                        .writeCharacteristic(data, callback, uuid_write);
+            }
+        }
+    }
+
+    /**
+     * read
+     *
+     * @param bleDevice
+     * @param uuid_service
+     * @param uuid_read
+     * @param callback
+     */
+    public void read(BleDevice bleDevice,
+                     String uuid_service,
+                     String uuid_read,
+                     BleReadCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleReadCallback can not be Null!");
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onReadFailure(new OtherException("This device is not connected!"));
+        } else {
+            bleBluetooth.newBleConnector()
+                    .withUUIDString(uuid_service, uuid_read)
+                    .readCharacteristic(callback, uuid_read);
+        }
+    }
+
+    /**
+     * read Rssi
+     *
+     * @param bleDevice
+     * @param callback
+     */
+    public void readRssi(BleDevice bleDevice,
+                         BleRssiCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleRssiCallback can not be Null!");
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onRssiFailure(new OtherException("This device is not connected!"));
+        } else {
+            bleBluetooth.newBleConnector().readRemoteRssi(callback);
+        }
+    }
+
+    /**
+     * set Mtu
+     *
+     * @param bleDevice
+     * @param mtu
+     * @param callback
+     */
+    public void setMtu(BleDevice bleDevice,
+                       int mtu,
+                       BleMtuChangedCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("BleMtuChangedCallback can not be Null!");
+        }
+
+        if (mtu > DEFAULT_MAX_MTU) {
+            BleLog.e("requiredMtu should lower than 512 !");
+            callback.onSetMTUFailure(new OtherException("requiredMtu should lower than 512 !"));
+            return;
+        }
+
+        if (mtu < DEFAULT_MTU) {
+            BleLog.e("requiredMtu should higher than 23 !");
+            callback.onSetMTUFailure(new OtherException("requiredMtu should higher than 23 !"));
+            return;
+        }
+
+        BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+        if (bleBluetooth == null) {
+            callback.onSetMTUFailure(new OtherException("This device is not connected!"));
+        } else {
+            bleBluetooth.newBleConnector().setMtu(mtu, callback);
+        }
+    }
+
+    /**
+     * requestConnectionPriority
+     *
+     * @param connectionPriority Request a specific connection priority. Must be one of
+     *                           {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
+     *                           {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
+     *                           or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
+     * @throws IllegalArgumentException If the parameters are outside of their
+     *                                  specified range.
+     */
+    public boolean requestConnectionPriority(BleDevice bleDevice, int connectionPriority) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            BleBluetooth bleBluetooth = multipleBluetoothController.getBleBluetooth(bleDevice);
+            if (bleBluetooth == null) {
+                return false;
+            } else {
+                return bleBluetooth.newBleConnector().requestConnectionPriority(connectionPriority);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * is support ble?
+     *
+     * @return
+     */
+    public boolean isSupportBle() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
+                && context.getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
+    }
+
+    /**
+     * Open bluetooth
+     */
+    public void enableBluetooth() {
+        if (bluetoothAdapter != null) {
+            bluetoothAdapter.enable();
+        }
+    }
+
+    /**
+     * Disable bluetooth
+     */
+    public void disableBluetooth() {
+        if (bluetoothAdapter != null) {
+            if (bluetoothAdapter.isEnabled())
+                bluetoothAdapter.disable();
+        }
+    }
+
+    /**
+     * judge Bluetooth is enable
+     *
+     * @return
+     */
+    public boolean isBlueEnable() {
+        return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
+    }
+
+
+    public BleDevice convertBleDevice(BluetoothDevice bluetoothDevice) {
+        return new BleDevice(bluetoothDevice);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public BleDevice convertBleDevice(ScanResult scanResult) {
+        if (scanResult == null) {
+            throw new IllegalArgumentException("scanResult can not be Null!");
+        }
+        BluetoothDevice bluetoothDevice = scanResult.getDevice();
+        int rssi = scanResult.getRssi();
+        ScanRecord scanRecord = scanResult.getScanRecord();
+        byte[] bytes = null;
+        if (scanRecord != null)
+            bytes = scanRecord.getBytes();
+        long timestampNanos = scanResult.getTimestampNanos();
+        return new BleDevice(bluetoothDevice, rssi, bytes, timestampNanos);
+    }
+
+    public BleBluetooth getBleBluetooth(BleDevice bleDevice) {
+        if (multipleBluetoothController != null) {
+            return multipleBluetoothController.getBleBluetooth(bleDevice);
+        }
+        return null;
+    }
+
+    public BluetoothGatt getBluetoothGatt(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            return bleBluetooth.getBluetoothGatt();
+        return null;
+    }
+
+    public List<BluetoothGattService> getBluetoothGattServices(BleDevice bleDevice) {
+        BluetoothGatt gatt = getBluetoothGatt(bleDevice);
+        if (gatt != null) {
+            return gatt.getServices();
+        }
+        return null;
+    }
+
+    public List<BluetoothGattCharacteristic> getBluetoothGattCharacteristics(BluetoothGattService service) {
+        return service.getCharacteristics();
+    }
+
+    public void removeConnectGattCallback(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeConnectGattCallback();
+    }
+
+    public void removeRssiCallback(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeRssiCallback();
+    }
+
+    public void removeMtuChangedCallback(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeMtuChangedCallback();
+    }
+
+    public void removeNotifyCallback(BleDevice bleDevice, String uuid_notify) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeNotifyCallback(uuid_notify);
+    }
+
+    public void removeIndicateCallback(BleDevice bleDevice, String uuid_indicate) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeIndicateCallback(uuid_indicate);
+    }
+
+    public void removeWriteCallback(BleDevice bleDevice, String uuid_write) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeWriteCallback(uuid_write);
+    }
+
+    public void removeReadCallback(BleDevice bleDevice, String uuid_read) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.removeReadCallback(uuid_read);
+    }
+
+    public void clearCharacterCallback(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = getBleBluetooth(bleDevice);
+        if (bleBluetooth != null)
+            bleBluetooth.clearCharacterCallback();
+    }
+
+    public BleScanState getScanSate() {
+        return BleScanner.getInstance().getScanState();
+    }
+
+    public List<BleDevice> getAllConnectedDevice() {
+        if (multipleBluetoothController == null)
+            return null;
+        return multipleBluetoothController.getDeviceList();
+    }
+
+    /**
+     * @param bleDevice
+     * @return State of the profile connection. One of
+     * {@link BluetoothProfile#STATE_CONNECTED},
+     * {@link BluetoothProfile#STATE_CONNECTING},
+     * {@link BluetoothProfile#STATE_DISCONNECTED},
+     * {@link BluetoothProfile#STATE_DISCONNECTING}
+     */
+    public int getConnectState(BleDevice bleDevice) {
+        if (bleDevice != null) {
+            return bluetoothManager.getConnectionState(bleDevice.getDevice(), BluetoothProfile.GATT);
+        } else {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    public boolean isConnected(BleDevice bleDevice) {
+        return getConnectState(bleDevice) == BluetoothProfile.STATE_CONNECTED;
+    }
+
+    public boolean isConnected(String mac) {
+        List<BleDevice> list = getAllConnectedDevice();
+        for (BleDevice bleDevice : list) {
+            if (bleDevice != null) {
+                if (bleDevice.getMac().equals(mac)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void disconnect(BleDevice bleDevice) {
+        if (multipleBluetoothController != null) {
+            multipleBluetoothController.disconnect(bleDevice);
+        }
+    }
+
+    public void disconnectAllDevice() {
+        if (multipleBluetoothController != null) {
+            multipleBluetoothController.disconnectAllDevice();
+        }
+    }
+
+    public void destroy() {
+        if (multipleBluetoothController != null) {
+            multipleBluetoothController.destroy();
+        }
+    }
+
+
+}

+ 612 - 0
FastBleLib/src/main/java/com/clj/fastble/bluetooth/BleBluetooth.java

@@ -0,0 +1,612 @@
+package com.clj.fastble.bluetooth;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothProfile;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.clj.fastble.BleManager;
+import com.clj.fastble.callback.BleGattCallback;
+import com.clj.fastble.callback.BleIndicateCallback;
+import com.clj.fastble.callback.BleMtuChangedCallback;
+import com.clj.fastble.callback.BleNotifyCallback;
+import com.clj.fastble.callback.BleReadCallback;
+import com.clj.fastble.callback.BleRssiCallback;
+import com.clj.fastble.callback.BleWriteCallback;
+import com.clj.fastble.data.BleConnectStateParameter;
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.data.BleMsg;
+import com.clj.fastble.exception.ConnectException;
+import com.clj.fastble.exception.OtherException;
+import com.clj.fastble.exception.TimeoutException;
+import com.clj.fastble.utils.BleLog;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class BleBluetooth {
+
+    private BleGattCallback bleGattCallback;
+    private BleRssiCallback bleRssiCallback;
+    private BleMtuChangedCallback bleMtuChangedCallback;
+    private final HashMap<String, BleNotifyCallback> bleNotifyCallbackHashMap = new HashMap<>();
+    private final HashMap<String, BleIndicateCallback> bleIndicateCallbackHashMap = new HashMap<>();
+    private final HashMap<String, BleWriteCallback> bleWriteCallbackHashMap = new HashMap<>();
+    private final HashMap<String, BleReadCallback> bleReadCallbackHashMap = new HashMap<>();
+
+    private LastState lastState;
+    private boolean isActiveDisconnect = false;
+    private final BleDevice bleDevice;
+    private BluetoothGatt bluetoothGatt;
+    private final MainHandler mainHandler = new MainHandler(Looper.getMainLooper());
+    private int connectRetryCount = 0;
+
+    public BleBluetooth(BleDevice bleDevice) {
+        this.bleDevice = bleDevice;
+    }
+
+    public BleConnector newBleConnector() {
+        return new BleConnector(this);
+    }
+
+    public synchronized void addConnectGattCallback(BleGattCallback callback) {
+        bleGattCallback = callback;
+    }
+
+    public synchronized void removeConnectGattCallback() {
+        bleGattCallback = null;
+    }
+
+    public synchronized void addNotifyCallback(String uuid, BleNotifyCallback bleNotifyCallback) {
+        bleNotifyCallbackHashMap.put(uuid, bleNotifyCallback);
+    }
+
+    public synchronized void addIndicateCallback(String uuid, BleIndicateCallback bleIndicateCallback) {
+        bleIndicateCallbackHashMap.put(uuid, bleIndicateCallback);
+    }
+
+    public synchronized void addWriteCallback(String uuid, BleWriteCallback bleWriteCallback) {
+        bleWriteCallbackHashMap.put(uuid, bleWriteCallback);
+    }
+
+    public synchronized void addReadCallback(String uuid, BleReadCallback bleReadCallback) {
+        bleReadCallbackHashMap.put(uuid, bleReadCallback);
+    }
+
+    public synchronized void removeNotifyCallback(String uuid) {
+        if (bleNotifyCallbackHashMap.containsKey(uuid))
+            bleNotifyCallbackHashMap.remove(uuid);
+    }
+
+    public synchronized void removeIndicateCallback(String uuid) {
+        if (bleIndicateCallbackHashMap.containsKey(uuid))
+            bleIndicateCallbackHashMap.remove(uuid);
+    }
+
+    public synchronized void removeWriteCallback(String uuid) {
+        if (bleWriteCallbackHashMap.containsKey(uuid))
+            bleWriteCallbackHashMap.remove(uuid);
+    }
+
+    public synchronized void removeReadCallback(String uuid) {
+        if (bleReadCallbackHashMap.containsKey(uuid))
+            bleReadCallbackHashMap.remove(uuid);
+    }
+
+    public synchronized void clearCharacterCallback() {
+        bleNotifyCallbackHashMap.clear();
+        bleIndicateCallbackHashMap.clear();
+        bleWriteCallbackHashMap.clear();
+        bleReadCallbackHashMap.clear();
+    }
+
+    public synchronized void addRssiCallback(BleRssiCallback callback) {
+        bleRssiCallback = callback;
+    }
+
+    public synchronized void removeRssiCallback() {
+        bleRssiCallback = null;
+    }
+
+    public synchronized void addMtuChangedCallback(BleMtuChangedCallback callback) {
+        bleMtuChangedCallback = callback;
+    }
+
+    public synchronized void removeMtuChangedCallback() {
+        bleMtuChangedCallback = null;
+    }
+
+
+    public String getDeviceKey() {
+        return bleDevice.getKey();
+    }
+
+    public BleDevice getDevice() {
+        return bleDevice;
+    }
+
+    public BluetoothGatt getBluetoothGatt() {
+        return bluetoothGatt;
+    }
+
+    public synchronized BluetoothGatt connect(BleDevice bleDevice,
+                                              boolean autoConnect,
+                                              BleGattCallback callback) {
+        return connect(bleDevice, autoConnect, callback, 0);
+    }
+
+    public synchronized BluetoothGatt connect(BleDevice bleDevice,
+                                              boolean autoConnect,
+                                              BleGattCallback callback,
+                                              int connectRetryCount) {
+        BleLog.i("connect device: " + bleDevice.getName()
+                + "\nmac: " + bleDevice.getMac()
+                + "\nautoConnect: " + autoConnect
+                + "\ncurrentThread: " + Thread.currentThread().getId()
+                + "\nconnectCount:" + (connectRetryCount + 1));
+        if (connectRetryCount == 0) {
+            this.connectRetryCount = 0;
+        }
+
+        addConnectGattCallback(callback);
+
+        lastState = LastState.CONNECT_CONNECTING;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            bluetoothGatt = bleDevice.getDevice().connectGatt(BleManager.getInstance().getContext(),
+                    autoConnect, coreGattCallback, TRANSPORT_LE);
+        } else {
+            bluetoothGatt = bleDevice.getDevice().connectGatt(BleManager.getInstance().getContext(),
+                    autoConnect, coreGattCallback);
+        }
+        if (bluetoothGatt != null) {
+            if (bleGattCallback != null) {
+                bleGattCallback.onStartConnect();
+            }
+            Message message = mainHandler.obtainMessage();
+            message.what = BleMsg.MSG_CONNECT_OVER_TIME;
+            mainHandler.sendMessageDelayed(message, BleManager.getInstance().getConnectOverTime());
+
+        } else {
+            disconnectGatt();
+            refreshDeviceCache();
+            closeBluetoothGatt();
+            lastState = LastState.CONNECT_FAILURE;
+            BleManager.getInstance().getMultipleBluetoothController().removeConnectingBle(BleBluetooth.this);
+            if (bleGattCallback != null)
+                bleGattCallback.onConnectFail(bleDevice, new OtherException("GATT connect exception occurred!"));
+
+        }
+        return bluetoothGatt;
+    }
+
+    public synchronized void disconnect() {
+        isActiveDisconnect = true;
+        disconnectGatt();
+    }
+
+    public synchronized void destroy() {
+        lastState = LastState.CONNECT_IDLE;
+        disconnectGatt();
+        refreshDeviceCache();
+        closeBluetoothGatt();
+        removeConnectGattCallback();
+        removeRssiCallback();
+        removeMtuChangedCallback();
+        clearCharacterCallback();
+        mainHandler.removeCallbacksAndMessages(null);
+    }
+
+    private synchronized void disconnectGatt() {
+        if (bluetoothGatt != null) {
+            bluetoothGatt.disconnect();
+        }
+    }
+
+    private synchronized void refreshDeviceCache() {
+        try {
+            final Method refresh = BluetoothGatt.class.getMethod("refresh");
+            if (refresh != null && bluetoothGatt != null) {
+                boolean success = (Boolean) refresh.invoke(bluetoothGatt);
+                BleLog.i("refreshDeviceCache, is success:  " + success);
+            }
+        } catch (Exception e) {
+            BleLog.i("exception occur while refreshing device: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    private synchronized void closeBluetoothGatt() {
+        if (bluetoothGatt != null) {
+            bluetoothGatt.close();
+        }
+    }
+
+    private final class MainHandler extends Handler {
+
+        MainHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case BleMsg.MSG_CONNECT_FAIL: {
+                    disconnectGatt();
+                    refreshDeviceCache();
+                    closeBluetoothGatt();
+
+                    if (connectRetryCount < BleManager.getInstance().getReConnectCount()) {
+                        BleLog.e("Connect fail, try reconnect " + BleManager.getInstance().getReConnectInterval() + " millisecond later");
+                        ++connectRetryCount;
+
+                        Message message = mainHandler.obtainMessage();
+                        message.what = BleMsg.MSG_RECONNECT;
+                        mainHandler.sendMessageDelayed(message, BleManager.getInstance().getReConnectInterval());
+                    } else {
+                        lastState = LastState.CONNECT_FAILURE;
+                        BleManager.getInstance().getMultipleBluetoothController().removeConnectingBle(BleBluetooth.this);
+
+                        BleConnectStateParameter para = (BleConnectStateParameter) msg.obj;
+                        int status = para.getStatus();
+                        if (bleGattCallback != null)
+                            bleGattCallback.onConnectFail(bleDevice, new ConnectException(bluetoothGatt, status));
+                    }
+                }
+                break;
+
+                case BleMsg.MSG_DISCONNECTED: {
+                    lastState = LastState.CONNECT_DISCONNECT;
+                    BleManager.getInstance().getMultipleBluetoothController().removeBleBluetooth(BleBluetooth.this);
+
+                    disconnect();
+                    refreshDeviceCache();
+                    closeBluetoothGatt();
+                    removeRssiCallback();
+                    removeMtuChangedCallback();
+                    clearCharacterCallback();
+                    mainHandler.removeCallbacksAndMessages(null);
+
+                    BleConnectStateParameter para = (BleConnectStateParameter) msg.obj;
+                    boolean isActive = para.isActive();
+                    int status = para.getStatus();
+                    if (bleGattCallback != null)
+                        bleGattCallback.onDisConnected(isActive, bleDevice, bluetoothGatt, status);
+                }
+                break;
+
+                case BleMsg.MSG_RECONNECT: {
+                    connect(bleDevice, false, bleGattCallback, connectRetryCount);
+                }
+                break;
+
+                case BleMsg.MSG_CONNECT_OVER_TIME: {
+                    disconnectGatt();
+                    refreshDeviceCache();
+                    closeBluetoothGatt();
+
+                    lastState = LastState.CONNECT_FAILURE;
+                    BleManager.getInstance().getMultipleBluetoothController().removeConnectingBle(BleBluetooth.this);
+
+                    if (bleGattCallback != null)
+                        bleGattCallback.onConnectFail(bleDevice, new TimeoutException());
+                }
+                break;
+
+                case BleMsg.MSG_DISCOVER_SERVICES: {
+                    if (bluetoothGatt != null) {
+                        boolean discoverServiceResult = bluetoothGatt.discoverServices();
+                        if (!discoverServiceResult) {
+                            Message message = mainHandler.obtainMessage();
+                            message.what = BleMsg.MSG_DISCOVER_FAIL;
+                            mainHandler.sendMessage(message);
+                        }
+                    } else {
+                        Message message = mainHandler.obtainMessage();
+                        message.what = BleMsg.MSG_DISCOVER_FAIL;
+                        mainHandler.sendMessage(message);
+                    }
+                }
+                break;
+
+                case BleMsg.MSG_DISCOVER_FAIL: {
+                    disconnectGatt();
+                    refreshDeviceCache();
+                    closeBluetoothGatt();
+
+                    lastState = LastState.CONNECT_FAILURE;
+                    BleManager.getInstance().getMultipleBluetoothController().removeConnectingBle(BleBluetooth.this);
+
+                    if (bleGattCallback != null)
+                        bleGattCallback.onConnectFail(bleDevice,
+                                new OtherException("GATT discover services exception occurred!"));
+                }
+                break;
+
+                case BleMsg.MSG_DISCOVER_SUCCESS: {
+                    lastState = LastState.CONNECT_CONNECTED;
+                    isActiveDisconnect = false;
+                    BleManager.getInstance().getMultipleBluetoothController().removeConnectingBle(BleBluetooth.this);
+                    BleManager.getInstance().getMultipleBluetoothController().addBleBluetooth(BleBluetooth.this);
+
+                    BleConnectStateParameter para = (BleConnectStateParameter) msg.obj;
+                    int status = para.getStatus();
+                    if (bleGattCallback != null)
+                        bleGattCallback.onConnectSuccess(bleDevice, bluetoothGatt, status);
+                }
+                break;
+
+                default:
+                    super.handleMessage(msg);
+                    break;
+            }
+        }
+    }
+
+    private BluetoothGattCallback coreGattCallback = new BluetoothGattCallback() {
+
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            super.onConnectionStateChange(gatt, status, newState);
+            BleLog.i("BluetoothGattCallback:onConnectionStateChange "
+                    + '\n' + "status: " + status
+                    + '\n' + "newState: " + newState
+                    + '\n' + "currentThread: " + Thread.currentThread().getId());
+
+            bluetoothGatt = gatt;
+
+            mainHandler.removeMessages(BleMsg.MSG_CONNECT_OVER_TIME);
+
+            if (newState == BluetoothProfile.STATE_CONNECTED) {
+                Message message = mainHandler.obtainMessage();
+                message.what = BleMsg.MSG_DISCOVER_SERVICES;
+                mainHandler.sendMessageDelayed(message, 500);
+
+            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                if (lastState == LastState.CONNECT_CONNECTING) {
+                    Message message = mainHandler.obtainMessage();
+                    message.what = BleMsg.MSG_CONNECT_FAIL;
+                    message.obj = new BleConnectStateParameter(status);
+                    mainHandler.sendMessage(message);
+
+                } else if (lastState == LastState.CONNECT_CONNECTED) {
+                    Message message = mainHandler.obtainMessage();
+                    message.what = BleMsg.MSG_DISCONNECTED;
+                    BleConnectStateParameter para = new BleConnectStateParameter(status);
+                    para.setActive(isActiveDisconnect);
+                    message.obj = para;
+                    mainHandler.sendMessage(message);
+                }
+            }
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            super.onServicesDiscovered(gatt, status);
+            BleLog.i("BluetoothGattCallback:onServicesDiscovered "
+                    + '\n' + "status: " + status
+                    + '\n' + "currentThread: " + Thread.currentThread().getId());
+
+            bluetoothGatt = gatt;
+
+            if (status == BluetoothGatt.GATT_SUCCESS) {
+                Message message = mainHandler.obtainMessage();
+                message.what = BleMsg.MSG_DISCOVER_SUCCESS;
+                message.obj = new BleConnectStateParameter(status);
+                mainHandler.sendMessage(message);
+
+            } else {
+                Message message = mainHandler.obtainMessage();
+                message.what = BleMsg.MSG_DISCOVER_FAIL;
+                mainHandler.sendMessage(message);
+            }
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+            super.onCharacteristicChanged(gatt, characteristic);
+
+            Iterator iterator = bleNotifyCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleNotifyCallback) {
+                    BleNotifyCallback bleNotifyCallback = (BleNotifyCallback) callback;
+                    if (characteristic.getUuid().toString().equalsIgnoreCase(bleNotifyCallback.getKey())) {
+                        Handler handler = bleNotifyCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_NOTIFY_DATA_CHANGE;
+                            message.obj = bleNotifyCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putByteArray(BleMsg.KEY_NOTIFY_BUNDLE_VALUE, characteristic.getValue());
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+
+            iterator = bleIndicateCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleIndicateCallback) {
+                    BleIndicateCallback bleIndicateCallback = (BleIndicateCallback) callback;
+                    if (characteristic.getUuid().toString().equalsIgnoreCase(bleIndicateCallback.getKey())) {
+                        Handler handler = bleIndicateCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_INDICATE_DATA_CHANGE;
+                            message.obj = bleIndicateCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putByteArray(BleMsg.KEY_INDICATE_BUNDLE_VALUE, characteristic.getValue());
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+            super.onDescriptorWrite(gatt, descriptor, status);
+
+            Iterator iterator = bleNotifyCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleNotifyCallback) {
+                    BleNotifyCallback bleNotifyCallback = (BleNotifyCallback) callback;
+                    if (descriptor.getCharacteristic().getUuid().toString().equalsIgnoreCase(bleNotifyCallback.getKey())) {
+                        Handler handler = bleNotifyCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_NOTIFY_RESULT;
+                            message.obj = bleNotifyCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putInt(BleMsg.KEY_NOTIFY_BUNDLE_STATUS, status);
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+
+            iterator = bleIndicateCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleIndicateCallback) {
+                    BleIndicateCallback bleIndicateCallback = (BleIndicateCallback) callback;
+                    if (descriptor.getCharacteristic().getUuid().toString().equalsIgnoreCase(bleIndicateCallback.getKey())) {
+                        Handler handler = bleIndicateCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_INDICATE_RESULT;
+                            message.obj = bleIndicateCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putInt(BleMsg.KEY_INDICATE_BUNDLE_STATUS, status);
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            super.onCharacteristicWrite(gatt, characteristic, status);
+
+            Iterator iterator = bleWriteCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleWriteCallback) {
+                    BleWriteCallback bleWriteCallback = (BleWriteCallback) callback;
+                    if (characteristic.getUuid().toString().equalsIgnoreCase(bleWriteCallback.getKey())) {
+                        Handler handler = bleWriteCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_WRITE_RESULT;
+                            message.obj = bleWriteCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putInt(BleMsg.KEY_WRITE_BUNDLE_STATUS, status);
+                            bundle.putByteArray(BleMsg.KEY_WRITE_BUNDLE_VALUE, characteristic.getValue());
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            super.onCharacteristicRead(gatt, characteristic, status);
+
+            Iterator iterator = bleReadCallbackHashMap.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                Object callback = entry.getValue();
+                if (callback instanceof BleReadCallback) {
+                    BleReadCallback bleReadCallback = (BleReadCallback) callback;
+                    if (characteristic.getUuid().toString().equalsIgnoreCase(bleReadCallback.getKey())) {
+                        Handler handler = bleReadCallback.getHandler();
+                        if (handler != null) {
+                            Message message = handler.obtainMessage();
+                            message.what = BleMsg.MSG_CHA_READ_RESULT;
+                            message.obj = bleReadCallback;
+                            Bundle bundle = new Bundle();
+                            bundle.putInt(BleMsg.KEY_READ_BUNDLE_STATUS, status);
+                            bundle.putByteArray(BleMsg.KEY_READ_BUNDLE_VALUE, characteristic.getValue());
+                            message.setData(bundle);
+                            handler.sendMessage(message);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+            super.onReadRemoteRssi(gatt, rssi, status);
+
+            if (bleRssiCallback != null) {
+                Handler handler = bleRssiCallback.getHandler();
+                if (handler != null) {
+                    Message message = handler.obtainMessage();
+                    message.what = BleMsg.MSG_READ_RSSI_RESULT;
+                    message.obj = bleRssiCallback;
+                    Bundle bundle = new Bundle();
+                    bundle.putInt(BleMsg.KEY_READ_RSSI_BUNDLE_STATUS, status);
+                    bundle.putInt(BleMsg.KEY_READ_RSSI_BUNDLE_VALUE, rssi);
+                    message.setData(bundle);
+                    handler.sendMessage(message);
+                }
+            }
+        }
+
+        @Override
+        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+            super.onMtuChanged(gatt, mtu, status);
+
+            if (bleMtuChangedCallback != null) {
+                Handler handler = bleMtuChangedCallback.getHandler();
+                if (handler != null) {
+                    Message message = handler.obtainMessage();
+                    message.what = BleMsg.MSG_SET_MTU_RESULT;
+                    message.obj = bleMtuChangedCallback;
+                    Bundle bundle = new Bundle();
+                    bundle.putInt(BleMsg.KEY_SET_MTU_BUNDLE_STATUS, status);
+                    bundle.putInt(BleMsg.KEY_SET_MTU_BUNDLE_VALUE, mtu);
+                    message.setData(bundle);
+                    handler.sendMessage(message);
+                }
+            }
+        }
+    };
+
+    enum LastState {
+        CONNECT_IDLE,
+        CONNECT_CONNECTING,
+        CONNECT_CONNECTED,
+        CONNECT_FAILURE,
+        CONNECT_DISCONNECT
+    }
+
+}

+ 608 - 0
FastBleLib/src/main/java/com/clj/fastble/bluetooth/BleConnector.java

@@ -0,0 +1,608 @@
+
+package com.clj.fastble.bluetooth;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.clj.fastble.BleManager;
+import com.clj.fastble.callback.BleIndicateCallback;
+import com.clj.fastble.callback.BleMtuChangedCallback;
+import com.clj.fastble.callback.BleNotifyCallback;
+import com.clj.fastble.callback.BleReadCallback;
+import com.clj.fastble.callback.BleRssiCallback;
+import com.clj.fastble.callback.BleWriteCallback;
+import com.clj.fastble.data.BleMsg;
+import com.clj.fastble.data.BleWriteState;
+import com.clj.fastble.exception.GattException;
+import com.clj.fastble.exception.OtherException;
+import com.clj.fastble.exception.TimeoutException;
+
+import java.util.UUID;
+
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class BleConnector {
+
+    private static final String UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb";
+
+    private BluetoothGatt mBluetoothGatt;
+    private BluetoothGattService mGattService;
+    private BluetoothGattCharacteristic mCharacteristic;
+    private BleBluetooth mBleBluetooth;
+    private Handler mHandler;
+
+    BleConnector(BleBluetooth bleBluetooth) {
+        this.mBleBluetooth = bleBluetooth;
+        this.mBluetoothGatt = bleBluetooth.getBluetoothGatt();
+        this.mHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                switch (msg.what) {
+
+                    case BleMsg.MSG_CHA_NOTIFY_START: {
+                        BleNotifyCallback notifyCallback = (BleNotifyCallback) msg.obj;
+                        if (notifyCallback != null)
+                            notifyCallback.onNotifyFailure(new TimeoutException());
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_NOTIFY_RESULT: {
+                        notifyMsgInit();
+
+                        BleNotifyCallback notifyCallback = (BleNotifyCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_NOTIFY_BUNDLE_STATUS);
+                        if (notifyCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                notifyCallback.onNotifySuccess();
+                            } else {
+                                notifyCallback.onNotifyFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_NOTIFY_DATA_CHANGE: {
+                        BleNotifyCallback notifyCallback = (BleNotifyCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        byte[] value = bundle.getByteArray(BleMsg.KEY_NOTIFY_BUNDLE_VALUE);
+                        if (notifyCallback != null) {
+                            notifyCallback.onCharacteristicChanged(value);
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_INDICATE_START: {
+                        BleIndicateCallback indicateCallback = (BleIndicateCallback) msg.obj;
+                        if (indicateCallback != null)
+                            indicateCallback.onIndicateFailure(new TimeoutException());
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_INDICATE_RESULT: {
+                        indicateMsgInit();
+
+                        BleIndicateCallback indicateCallback = (BleIndicateCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_INDICATE_BUNDLE_STATUS);
+                        if (indicateCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                indicateCallback.onIndicateSuccess();
+                            } else {
+                                indicateCallback.onIndicateFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_INDICATE_DATA_CHANGE: {
+                        BleIndicateCallback indicateCallback = (BleIndicateCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        byte[] value = bundle.getByteArray(BleMsg.KEY_INDICATE_BUNDLE_VALUE);
+                        if (indicateCallback != null) {
+                            indicateCallback.onCharacteristicChanged(value);
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_WRITE_START: {
+                        BleWriteCallback writeCallback = (BleWriteCallback) msg.obj;
+                        if (writeCallback != null) {
+                            writeCallback.onWriteFailure(new TimeoutException());
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_WRITE_RESULT: {
+                        writeMsgInit();
+
+                        BleWriteCallback writeCallback = (BleWriteCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_WRITE_BUNDLE_STATUS);
+                        byte[] value = bundle.getByteArray(BleMsg.KEY_WRITE_BUNDLE_VALUE);
+                        if (writeCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                writeCallback.onWriteSuccess(BleWriteState.DATA_WRITE_SINGLE, BleWriteState.DATA_WRITE_SINGLE, value);
+                            } else {
+                                writeCallback.onWriteFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_READ_START: {
+                        BleReadCallback readCallback = (BleReadCallback) msg.obj;
+                        if (readCallback != null)
+                            readCallback.onReadFailure(new TimeoutException());
+                        break;
+                    }
+
+                    case BleMsg.MSG_CHA_READ_RESULT: {
+                        readMsgInit();
+
+                        BleReadCallback readCallback = (BleReadCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_READ_BUNDLE_STATUS);
+                        byte[] value = bundle.getByteArray(BleMsg.KEY_READ_BUNDLE_VALUE);
+                        if (readCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                readCallback.onReadSuccess(value);
+                            } else {
+                                readCallback.onReadFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_READ_RSSI_START: {
+                        BleRssiCallback rssiCallback = (BleRssiCallback) msg.obj;
+                        if (rssiCallback != null)
+                            rssiCallback.onRssiFailure(new TimeoutException());
+                        break;
+                    }
+
+                    case BleMsg.MSG_READ_RSSI_RESULT: {
+                        rssiMsgInit();
+
+                        BleRssiCallback rssiCallback = (BleRssiCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_READ_RSSI_BUNDLE_STATUS);
+                        int value = bundle.getInt(BleMsg.KEY_READ_RSSI_BUNDLE_VALUE);
+                        if (rssiCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                rssiCallback.onRssiSuccess(value);
+                            } else {
+                                rssiCallback.onRssiFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+
+                    case BleMsg.MSG_SET_MTU_START: {
+                        BleMtuChangedCallback mtuChangedCallback = (BleMtuChangedCallback) msg.obj;
+                        if (mtuChangedCallback != null)
+                            mtuChangedCallback.onSetMTUFailure(new TimeoutException());
+                        break;
+                    }
+
+                    case BleMsg.MSG_SET_MTU_RESULT: {
+                        mtuChangedMsgInit();
+
+                        BleMtuChangedCallback mtuChangedCallback = (BleMtuChangedCallback) msg.obj;
+                        Bundle bundle = msg.getData();
+                        int status = bundle.getInt(BleMsg.KEY_SET_MTU_BUNDLE_STATUS);
+                        int value = bundle.getInt(BleMsg.KEY_SET_MTU_BUNDLE_VALUE);
+                        if (mtuChangedCallback != null) {
+                            if (status == BluetoothGatt.GATT_SUCCESS) {
+                                mtuChangedCallback.onMtuChanged(value);
+                            } else {
+                                mtuChangedCallback.onSetMTUFailure(new GattException(status));
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+        };
+
+    }
+
+    private BleConnector withUUID(UUID serviceUUID, UUID characteristicUUID) {
+        if (serviceUUID != null && mBluetoothGatt != null) {
+            mGattService = mBluetoothGatt.getService(serviceUUID);
+        }
+        if (mGattService != null && characteristicUUID != null) {
+            mCharacteristic = mGattService.getCharacteristic(characteristicUUID);
+        }
+        return this;
+    }
+
+    public BleConnector withUUIDString(String serviceUUID, String characteristicUUID) {
+        return withUUID(formUUID(serviceUUID), formUUID(characteristicUUID));
+    }
+
+    private UUID formUUID(String uuid) {
+        return uuid == null ? null : UUID.fromString(uuid);
+    }
+
+
+    /*------------------------------- main operation ----------------------------------- */
+
+
+    /**
+     * notify
+     */
+    public void enableCharacteristicNotify(BleNotifyCallback bleNotifyCallback, String uuid_notify,
+                                           boolean userCharacteristicDescriptor) {
+        if (mCharacteristic != null
+                && (mCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
+
+            handleCharacteristicNotifyCallback(bleNotifyCallback, uuid_notify);
+            setCharacteristicNotification(mBluetoothGatt, mCharacteristic, userCharacteristicDescriptor, true, bleNotifyCallback);
+        } else {
+            if (bleNotifyCallback != null)
+                bleNotifyCallback.onNotifyFailure(new OtherException("this characteristic not support notify!"));
+        }
+    }
+
+    /**
+     * stop notify
+     */
+    public boolean disableCharacteristicNotify(boolean useCharacteristicDescriptor) {
+        if (mCharacteristic != null
+                && (mCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
+            return setCharacteristicNotification(mBluetoothGatt, mCharacteristic,
+                    useCharacteristicDescriptor, false, null);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * notify setting
+     */
+    private boolean setCharacteristicNotification(BluetoothGatt gatt,
+                                                  BluetoothGattCharacteristic characteristic,
+                                                  boolean useCharacteristicDescriptor,
+                                                  boolean enable,
+                                                  BleNotifyCallback bleNotifyCallback) {
+        if (gatt == null || characteristic == null) {
+            notifyMsgInit();
+            if (bleNotifyCallback != null)
+                bleNotifyCallback.onNotifyFailure(new OtherException("gatt or characteristic equal null"));
+            return false;
+        }
+
+        boolean success1 = gatt.setCharacteristicNotification(characteristic, enable);
+        if (!success1) {
+            notifyMsgInit();
+            if (bleNotifyCallback != null)
+                bleNotifyCallback.onNotifyFailure(new OtherException("gatt setCharacteristicNotification fail"));
+            return false;
+        }
+
+        BluetoothGattDescriptor descriptor;
+        if (useCharacteristicDescriptor) {
+            descriptor = characteristic.getDescriptor(characteristic.getUuid());
+        } else {
+            descriptor = characteristic.getDescriptor(formUUID(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
+        }
+        if (descriptor == null) {
+            notifyMsgInit();
+            if (bleNotifyCallback != null)
+                bleNotifyCallback.onNotifyFailure(new OtherException("descriptor equals null"));
+            return false;
+        } else {
+            descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :
+                    BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
+            boolean success2 = gatt.writeDescriptor(descriptor);
+            if (!success2) {
+                notifyMsgInit();
+                if (bleNotifyCallback != null)
+                    bleNotifyCallback.onNotifyFailure(new OtherException("gatt writeDescriptor fail"));
+            }
+            return success2;
+        }
+    }
+
+    /**
+     * indicate
+     */
+    public void enableCharacteristicIndicate(BleIndicateCallback bleIndicateCallback, String uuid_indicate,
+                                             boolean useCharacteristicDescriptor) {
+        if (mCharacteristic != null
+                && (mCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
+            handleCharacteristicIndicateCallback(bleIndicateCallback, uuid_indicate);
+            setCharacteristicIndication(mBluetoothGatt, mCharacteristic,
+                    useCharacteristicDescriptor, true, bleIndicateCallback);
+        } else {
+            if (bleIndicateCallback != null)
+                bleIndicateCallback.onIndicateFailure(new OtherException("this characteristic not support indicate!"));
+        }
+    }
+
+
+    /**
+     * stop indicate
+     */
+    public boolean disableCharacteristicIndicate(boolean userCharacteristicDescriptor) {
+        if (mCharacteristic != null
+                && (mCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
+            return setCharacteristicIndication(mBluetoothGatt, mCharacteristic,
+                    userCharacteristicDescriptor, false, null);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * indicate setting
+     */
+    private boolean setCharacteristicIndication(BluetoothGatt gatt,
+                                                BluetoothGattCharacteristic characteristic,
+                                                boolean useCharacteristicDescriptor,
+                                                boolean enable,
+                                                BleIndicateCallback bleIndicateCallback) {
+        if (gatt == null || characteristic == null) {
+            indicateMsgInit();
+            if (bleIndicateCallback != null)
+                bleIndicateCallback.onIndicateFailure(new OtherException("gatt or characteristic equal null"));
+            return false;
+        }
+
+        boolean success1 = gatt.setCharacteristicNotification(characteristic, enable);
+        if (!success1) {
+            indicateMsgInit();
+            if (bleIndicateCallback != null)
+                bleIndicateCallback.onIndicateFailure(new OtherException("gatt setCharacteristicNotification fail"));
+            return false;
+        }
+
+        BluetoothGattDescriptor descriptor;
+        if (useCharacteristicDescriptor) {
+            descriptor = characteristic.getDescriptor(characteristic.getUuid());
+        } else {
+            descriptor = characteristic.getDescriptor(formUUID(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
+        }
+        if (descriptor == null) {
+            indicateMsgInit();
+            if (bleIndicateCallback != null)
+                bleIndicateCallback.onIndicateFailure(new OtherException("descriptor equals null"));
+            return false;
+        } else {
+            descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE :
+                    BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
+            boolean success2 = gatt.writeDescriptor(descriptor);
+            if (!success2) {
+                indicateMsgInit();
+                if (bleIndicateCallback != null)
+                    bleIndicateCallback.onIndicateFailure(new OtherException("gatt writeDescriptor fail"));
+            }
+            return success2;
+        }
+    }
+
+    /**
+     * write
+     */
+    public void writeCharacteristic(byte[] data, BleWriteCallback bleWriteCallback, String uuid_write) {
+        if (data == null || data.length <= 0) {
+            if (bleWriteCallback != null)
+                bleWriteCallback.onWriteFailure(new OtherException("the data to be written is empty"));
+            return;
+        }
+
+        if (mCharacteristic == null
+                || (mCharacteristic.getProperties() & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) == 0) {
+            if (bleWriteCallback != null)
+                bleWriteCallback.onWriteFailure(new OtherException("this characteristic not support write!"));
+            return;
+        }
+
+        if (mCharacteristic.setValue(data)) {
+            handleCharacteristicWriteCallback(bleWriteCallback, uuid_write);
+            if (!mBluetoothGatt.writeCharacteristic(mCharacteristic)) {
+                writeMsgInit();
+                if (bleWriteCallback != null)
+                    bleWriteCallback.onWriteFailure(new OtherException("gatt writeCharacteristic fail"));
+            }
+        } else {
+            if (bleWriteCallback != null)
+                bleWriteCallback.onWriteFailure(new OtherException("Updates the locally stored value of this characteristic fail"));
+        }
+    }
+
+    /**
+     * read
+     */
+    public void readCharacteristic(BleReadCallback bleReadCallback, String uuid_read) {
+        if (mCharacteristic != null
+                && (mCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
+
+            handleCharacteristicReadCallback(bleReadCallback, uuid_read);
+            if (!mBluetoothGatt.readCharacteristic(mCharacteristic)) {
+                readMsgInit();
+                if (bleReadCallback != null)
+                    bleReadCallback.onReadFailure(new OtherException("gatt readCharacteristic fail"));
+            }
+        } else {
+            if (bleReadCallback != null)
+                bleReadCallback.onReadFailure(new OtherException("this characteristic not support read!"));
+        }
+    }
+
+    /**
+     * rssi
+     */
+    public void readRemoteRssi(BleRssiCallback bleRssiCallback) {
+        handleRSSIReadCallback(bleRssiCallback);
+        if (!mBluetoothGatt.readRemoteRssi()) {
+            rssiMsgInit();
+            if (bleRssiCallback != null)
+                bleRssiCallback.onRssiFailure(new OtherException("gatt readRemoteRssi fail"));
+        }
+    }
+
+    /**
+     * set mtu
+     */
+    public void setMtu(int requiredMtu, BleMtuChangedCallback bleMtuChangedCallback) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            handleSetMtuCallback(bleMtuChangedCallback);
+            if (!mBluetoothGatt.requestMtu(requiredMtu)) {
+                mtuChangedMsgInit();
+                if (bleMtuChangedCallback != null)
+                    bleMtuChangedCallback.onSetMTUFailure(new OtherException("gatt requestMtu fail"));
+            }
+        } else {
+            if (bleMtuChangedCallback != null)
+                bleMtuChangedCallback.onSetMTUFailure(new OtherException("API level lower than 21"));
+        }
+    }
+
+    /**
+     * requestConnectionPriority
+     *
+     * @param connectionPriority Request a specific connection priority. Must be one of
+     *                           {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
+     *                           {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
+     *                           or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
+     * @throws IllegalArgumentException If the parameters are outside of their
+     *                                  specified range.
+     */
+    public boolean requestConnectionPriority(int connectionPriority) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return mBluetoothGatt.requestConnectionPriority(connectionPriority);
+        }
+        return false;
+    }
+
+
+    /**************************************** Handle call back ******************************************/
+
+    /**
+     * notify
+     */
+    private void handleCharacteristicNotifyCallback(BleNotifyCallback bleNotifyCallback,
+                                                    String uuid_notify) {
+        if (bleNotifyCallback != null) {
+            notifyMsgInit();
+            bleNotifyCallback.setKey(uuid_notify);
+            bleNotifyCallback.setHandler(mHandler);
+            mBleBluetooth.addNotifyCallback(uuid_notify, bleNotifyCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_CHA_NOTIFY_START, bleNotifyCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    /**
+     * indicate
+     */
+    private void handleCharacteristicIndicateCallback(BleIndicateCallback bleIndicateCallback,
+                                                      String uuid_indicate) {
+        if (bleIndicateCallback != null) {
+            indicateMsgInit();
+            bleIndicateCallback.setKey(uuid_indicate);
+            bleIndicateCallback.setHandler(mHandler);
+            mBleBluetooth.addIndicateCallback(uuid_indicate, bleIndicateCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_CHA_INDICATE_START, bleIndicateCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    /**
+     * write
+     */
+    private void handleCharacteristicWriteCallback(BleWriteCallback bleWriteCallback,
+                                                   String uuid_write) {
+        if (bleWriteCallback != null) {
+            writeMsgInit();
+            bleWriteCallback.setKey(uuid_write);
+            bleWriteCallback.setHandler(mHandler);
+            mBleBluetooth.addWriteCallback(uuid_write, bleWriteCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_CHA_WRITE_START, bleWriteCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    /**
+     * read
+     */
+    private void handleCharacteristicReadCallback(BleReadCallback bleReadCallback,
+                                                  String uuid_read) {
+        if (bleReadCallback != null) {
+            readMsgInit();
+            bleReadCallback.setKey(uuid_read);
+            bleReadCallback.setHandler(mHandler);
+            mBleBluetooth.addReadCallback(uuid_read, bleReadCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_CHA_READ_START, bleReadCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    /**
+     * rssi
+     */
+    private void handleRSSIReadCallback(BleRssiCallback bleRssiCallback) {
+        if (bleRssiCallback != null) {
+            rssiMsgInit();
+            bleRssiCallback.setHandler(mHandler);
+            mBleBluetooth.addRssiCallback(bleRssiCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_READ_RSSI_START, bleRssiCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    /**
+     * set mtu
+     */
+    private void handleSetMtuCallback(BleMtuChangedCallback bleMtuChangedCallback) {
+        if (bleMtuChangedCallback != null) {
+            mtuChangedMsgInit();
+            bleMtuChangedCallback.setHandler(mHandler);
+            mBleBluetooth.addMtuChangedCallback(bleMtuChangedCallback);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(BleMsg.MSG_SET_MTU_START, bleMtuChangedCallback),
+                    BleManager.getInstance().getOperateTimeout());
+        }
+    }
+
+    public void notifyMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_CHA_NOTIFY_START);
+    }
+
+    public void indicateMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_CHA_INDICATE_START);
+    }
+
+    public void writeMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_CHA_WRITE_START);
+    }
+
+    public void readMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_CHA_READ_START);
+    }
+
+    public void rssiMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_READ_RSSI_START);
+    }
+
+    public void mtuChangedMsgInit() {
+        mHandler.removeMessages(BleMsg.MSG_SET_MTU_START);
+    }
+
+}

+ 139 - 0
FastBleLib/src/main/java/com/clj/fastble/bluetooth/MultipleBluetoothController.java

@@ -0,0 +1,139 @@
+package com.clj.fastble.bluetooth;
+
+
+import android.bluetooth.BluetoothDevice;
+import android.os.Build;
+
+import com.clj.fastble.BleManager;
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.utils.BleLruHashMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MultipleBluetoothController {
+
+    private final BleLruHashMap<String, BleBluetooth> bleLruHashMap;
+    private final HashMap<String, BleBluetooth> bleTempHashMap;
+
+    public MultipleBluetoothController() {
+        bleLruHashMap = new BleLruHashMap<>(BleManager.getInstance().getMaxConnectCount());
+        bleTempHashMap = new HashMap<>();
+    }
+
+    public synchronized BleBluetooth buildConnectingBle(BleDevice bleDevice) {
+        BleBluetooth bleBluetooth = new BleBluetooth(bleDevice);
+        if (!bleTempHashMap.containsKey(bleBluetooth.getDeviceKey())) {
+            bleTempHashMap.put(bleBluetooth.getDeviceKey(), bleBluetooth);
+        }
+        return bleBluetooth;
+    }
+
+    public synchronized void removeConnectingBle(BleBluetooth bleBluetooth) {
+        if (bleBluetooth == null) {
+            return;
+        }
+        if (bleTempHashMap.containsKey(bleBluetooth.getDeviceKey())) {
+            bleTempHashMap.remove(bleBluetooth.getDeviceKey());
+        }
+    }
+
+    public synchronized void addBleBluetooth(BleBluetooth bleBluetooth) {
+        if (bleBluetooth == null) {
+            return;
+        }
+        if (!bleLruHashMap.containsKey(bleBluetooth.getDeviceKey())) {
+            bleLruHashMap.put(bleBluetooth.getDeviceKey(), bleBluetooth);
+        }
+    }
+
+    public synchronized void removeBleBluetooth(BleBluetooth bleBluetooth) {
+        if (bleBluetooth == null) {
+            return;
+        }
+        if (bleLruHashMap.containsKey(bleBluetooth.getDeviceKey())) {
+            bleLruHashMap.remove(bleBluetooth.getDeviceKey());
+        }
+    }
+
+    public synchronized boolean isContainDevice(BleDevice bleDevice) {
+        return bleDevice != null && bleLruHashMap.containsKey(bleDevice.getKey());
+    }
+
+    public synchronized boolean isContainDevice(BluetoothDevice bluetoothDevice) {
+        return bluetoothDevice != null && bleLruHashMap.containsKey(bluetoothDevice.getName() + bluetoothDevice.getAddress());
+    }
+
+    public synchronized BleBluetooth getBleBluetooth(BleDevice bleDevice) {
+        if (bleDevice != null) {
+            if (bleLruHashMap.containsKey(bleDevice.getKey())) {
+                return bleLruHashMap.get(bleDevice.getKey());
+            }
+        }
+        return null;
+    }
+
+    public synchronized void disconnect(BleDevice bleDevice) {
+        if (isContainDevice(bleDevice)) {
+            getBleBluetooth(bleDevice).disconnect();
+        }
+    }
+
+    public synchronized void disconnectAllDevice() {
+        for (Map.Entry<String, BleBluetooth> stringBleBluetoothEntry : bleLruHashMap.entrySet()) {
+            stringBleBluetoothEntry.getValue().disconnect();
+        }
+        bleLruHashMap.clear();
+    }
+
+    public synchronized void destroy() {
+        for (Map.Entry<String, BleBluetooth> stringBleBluetoothEntry : bleLruHashMap.entrySet()) {
+            stringBleBluetoothEntry.getValue().destroy();
+        }
+        bleLruHashMap.clear();
+        for (Map.Entry<String, BleBluetooth> stringBleBluetoothEntry : bleTempHashMap.entrySet()) {
+            stringBleBluetoothEntry.getValue().destroy();
+        }
+        bleTempHashMap.clear();
+    }
+
+    public synchronized List<BleBluetooth> getBleBluetoothList() {
+        List<BleBluetooth> bleBluetoothList = new ArrayList<>(bleLruHashMap.values());
+        Collections.sort(bleBluetoothList, new Comparator<BleBluetooth>() {
+            @Override
+            public int compare(BleBluetooth lhs, BleBluetooth rhs) {
+                return lhs.getDeviceKey().compareToIgnoreCase(rhs.getDeviceKey());
+            }
+        });
+        return bleBluetoothList;
+    }
+
+    public synchronized List<BleDevice> getDeviceList() {
+        refreshConnectedDevice();
+        List<BleDevice> deviceList = new ArrayList<>();
+        for (BleBluetooth BleBluetooth : getBleBluetoothList()) {
+            if (BleBluetooth != null) {
+                deviceList.add(BleBluetooth.getDevice());
+            }
+        }
+        return deviceList;
+    }
+
+    public void refreshConnectedDevice() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            List<BleBluetooth> bluetoothList = getBleBluetoothList();
+            for (int i = 0; bluetoothList != null && i < bluetoothList.size(); i++) {
+                BleBluetooth bleBluetooth = bluetoothList.get(i);
+                if (!BleManager.getInstance().isConnected(bleBluetooth.getDevice())) {
+                    removeBleBluetooth(bleBluetooth);
+                }
+            }
+        }
+    }
+
+
+}

+ 158 - 0
FastBleLib/src/main/java/com/clj/fastble/bluetooth/SplitWriter.java

@@ -0,0 +1,158 @@
+package com.clj.fastble.bluetooth;
+
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+
+import com.clj.fastble.BleManager;
+import com.clj.fastble.callback.BleWriteCallback;
+import com.clj.fastble.data.BleMsg;
+import com.clj.fastble.exception.BleException;
+import com.clj.fastble.exception.OtherException;
+import com.clj.fastble.utils.BleLog;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+public class SplitWriter {
+
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+
+    private BleBluetooth mBleBluetooth;
+    private String mUuid_service;
+    private String mUuid_write;
+    private byte[] mData;
+    private int mCount;
+    private boolean mSendNextWhenLastSuccess;
+    private long mIntervalBetweenTwoPackage;
+    private BleWriteCallback mCallback;
+    private Queue<byte[]> mDataQueue;
+    private int mTotalNum;
+
+    public SplitWriter() {
+        mHandlerThread = new HandlerThread("splitWriter");
+        mHandlerThread.start();
+
+        mHandler = new Handler(mHandlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                if (msg.what == BleMsg.MSG_SPLIT_WRITE_NEXT) {
+                    write();
+                }
+            }
+        };
+    }
+
+    public void splitWrite(BleBluetooth bleBluetooth,
+                           String uuid_service,
+                           String uuid_write,
+                           byte[] data,
+                           boolean sendNextWhenLastSuccess,
+                           long intervalBetweenTwoPackage,
+                           BleWriteCallback callback) {
+        mBleBluetooth = bleBluetooth;
+        mUuid_service = uuid_service;
+        mUuid_write = uuid_write;
+        mData = data;
+        mSendNextWhenLastSuccess = sendNextWhenLastSuccess;
+        mIntervalBetweenTwoPackage = intervalBetweenTwoPackage;
+        mCount = BleManager.getInstance().getSplitWriteNum();
+        mCallback = callback;
+
+        splitWrite();
+    }
+
+    private void splitWrite() {
+        if (mData == null) {
+            throw new IllegalArgumentException("data is Null!");
+        }
+        if (mCount < 1) {
+            throw new IllegalArgumentException("split count should higher than 0!");
+        }
+        mDataQueue = splitByte(mData, mCount);
+        mTotalNum = mDataQueue.size();
+        write();
+    }
+
+    private void write() {
+        if (mDataQueue.peek() == null) {
+            release();
+            return;
+        }
+
+        byte[] data = mDataQueue.poll();
+        mBleBluetooth.newBleConnector()
+                .withUUIDString(mUuid_service, mUuid_write)
+                .writeCharacteristic(
+                        data,
+                        new BleWriteCallback() {
+                            @Override
+                            public void onWriteSuccess(int current, int total, byte[] justWrite) {
+                                int position = mTotalNum - mDataQueue.size();
+                                if (mCallback != null) {
+                                    mCallback.onWriteSuccess(position, mTotalNum, justWrite);
+                                }
+                                if (mSendNextWhenLastSuccess) {
+                                    Message message = mHandler.obtainMessage(BleMsg.MSG_SPLIT_WRITE_NEXT);
+                                    mHandler.sendMessageDelayed(message, mIntervalBetweenTwoPackage);
+                                }
+                            }
+
+                            @Override
+                            public void onWriteFailure(BleException exception) {
+                                if (mCallback != null) {
+                                    mCallback.onWriteFailure(new OtherException("exception occur while writing: " + exception.getDescription()));
+                                }
+                                if (mSendNextWhenLastSuccess) {
+                                    Message message = mHandler.obtainMessage(BleMsg.MSG_SPLIT_WRITE_NEXT);
+                                    mHandler.sendMessageDelayed(message, mIntervalBetweenTwoPackage);
+                                }
+                            }
+                        },
+                        mUuid_write);
+
+        if (!mSendNextWhenLastSuccess) {
+            Message message = mHandler.obtainMessage(BleMsg.MSG_SPLIT_WRITE_NEXT);
+            mHandler.sendMessageDelayed(message, mIntervalBetweenTwoPackage);
+        }
+    }
+
+    private void release() {
+        mHandlerThread.quit();
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    private static Queue<byte[]> splitByte(byte[] data, int count) {
+        if (count > 20) {
+            BleLog.w("Be careful: split count beyond 20! Ensure MTU higher than 23!");
+        }
+        Queue<byte[]> byteQueue = new LinkedList<>();
+        int pkgCount;
+        if (data.length % count == 0) {
+            pkgCount = data.length / count;
+        } else {
+            pkgCount = Math.round(data.length / count + 1);
+        }
+
+        if (pkgCount > 0) {
+            for (int i = 0; i < pkgCount; i++) {
+                byte[] dataPkg;
+                int j;
+                if (pkgCount == 1 || i == pkgCount - 1) {
+                    j = data.length % count == 0 ? count : data.length % count;
+                    System.arraycopy(data, i * count, dataPkg = new byte[j], 0, j);
+                } else {
+                    System.arraycopy(data, i * count, dataPkg = new byte[count], 0, count);
+                }
+                byteQueue.offer(dataPkg);
+            }
+        }
+
+        return byteQueue;
+    }
+
+
+}

+ 27 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleBaseCallback.java

@@ -0,0 +1,27 @@
+package com.clj.fastble.callback;
+
+
+import android.os.Handler;
+
+public abstract class BleBaseCallback {
+
+    private String key;
+    private Handler handler;
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public Handler getHandler() {
+        return handler;
+    }
+
+    public void setHandler(Handler handler) {
+        this.handler = handler;
+    }
+
+}

+ 24 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleGattCallback.java

@@ -0,0 +1,24 @@
+
+package com.clj.fastble.callback;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.os.Build;
+
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.exception.BleException;
+
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public abstract class BleGattCallback extends BluetoothGattCallback {
+
+    public abstract void onStartConnect();
+
+    public abstract void onConnectFail(BleDevice bleDevice, BleException exception);
+
+    public abstract void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status);
+
+    public abstract void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status);
+
+}

+ 13 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleIndicateCallback.java

@@ -0,0 +1,13 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleIndicateCallback extends BleBaseCallback{
+
+    public abstract void onIndicateSuccess();
+
+    public abstract void onIndicateFailure(BleException exception);
+
+    public abstract void onCharacteristicChanged(byte[] data);
+}

+ 12 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleMtuChangedCallback.java

@@ -0,0 +1,12 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleMtuChangedCallback extends BleBaseCallback {
+
+    public abstract void onSetMTUFailure(BleException exception);
+
+    public abstract void onMtuChanged(int mtu);
+
+}

+ 14 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleNotifyCallback.java

@@ -0,0 +1,14 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleNotifyCallback extends BleBaseCallback {
+
+    public abstract void onNotifySuccess();
+
+    public abstract void onNotifyFailure(BleException exception);
+
+    public abstract void onCharacteristicChanged(byte[] data);
+
+}

+ 12 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleReadCallback.java

@@ -0,0 +1,12 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleReadCallback extends BleBaseCallback {
+
+    public abstract void onReadSuccess(byte[] data);
+
+    public abstract void onReadFailure(BleException exception);
+
+}

+ 12 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleRssiCallback.java

@@ -0,0 +1,12 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleRssiCallback extends BleBaseCallback{
+
+    public abstract void onRssiFailure(BleException exception);
+
+    public abstract void onRssiSuccess(int rssi);
+
+}

+ 13 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleScanAndConnectCallback.java

@@ -0,0 +1,13 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.data.BleDevice;
+
+public abstract class BleScanAndConnectCallback extends BleGattCallback implements BleScanPresenterImp {
+
+    public abstract void onScanFinished(BleDevice scanResult);
+
+    public void onLeScan(BleDevice bleDevice) {
+    }
+
+}

+ 14 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleScanCallback.java

@@ -0,0 +1,14 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.data.BleDevice;
+
+import java.util.List;
+
+public abstract class BleScanCallback implements BleScanPresenterImp {
+
+    public abstract void onScanFinished(List<BleDevice> scanResultList);
+
+    public void onLeScan(BleDevice bleDevice) {
+    }
+}

+ 11 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleScanPresenterImp.java

@@ -0,0 +1,11 @@
+package com.clj.fastble.callback;
+
+import com.clj.fastble.data.BleDevice;
+
+public interface BleScanPresenterImp {
+
+    void onScanStarted(boolean success);
+
+    void onScanning(BleDevice bleDevice);
+
+}

+ 12 - 0
FastBleLib/src/main/java/com/clj/fastble/callback/BleWriteCallback.java

@@ -0,0 +1,12 @@
+package com.clj.fastble.callback;
+
+
+import com.clj.fastble.exception.BleException;
+
+public abstract class BleWriteCallback extends BleBaseCallback{
+
+    public abstract void onWriteSuccess(int current, int total, byte[] justWrite);
+
+    public abstract void onWriteFailure(BleException exception);
+
+}

+ 29 - 0
FastBleLib/src/main/java/com/clj/fastble/data/BleConnectStateParameter.java

@@ -0,0 +1,29 @@
+package com.clj.fastble.data;
+
+
+public class BleConnectStateParameter {
+
+    private int status;
+    private boolean isActive;
+
+    public BleConnectStateParameter(int status) {
+        this.status = status;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public boolean isActive() {
+        return isActive;
+    }
+
+    public void setActive(boolean active) {
+        isActive = active;
+    }
+
+}

+ 112 - 0
FastBleLib/src/main/java/com/clj/fastble/data/BleDevice.java

@@ -0,0 +1,112 @@
+package com.clj.fastble.data;
+
+
+import android.bluetooth.BluetoothDevice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class BleDevice implements Parcelable {
+
+    private BluetoothDevice mDevice;
+    private byte[] mScanRecord;
+    private int mRssi;
+    private long mTimestampNanos;
+
+    public BleDevice(BluetoothDevice device) {
+        mDevice = device;
+    }
+
+    public BleDevice(BluetoothDevice device, int rssi, byte[] scanRecord, long timestampNanos) {
+        mDevice = device;
+        mScanRecord = scanRecord;
+        mRssi = rssi;
+        mTimestampNanos = timestampNanos;
+    }
+
+    protected BleDevice(Parcel in) {
+        mDevice = in.readParcelable(BluetoothDevice.class.getClassLoader());
+        mScanRecord = in.createByteArray();
+        mRssi = in.readInt();
+        mTimestampNanos = in.readLong();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mDevice, flags);
+        dest.writeByteArray(mScanRecord);
+        dest.writeInt(mRssi);
+        dest.writeLong(mTimestampNanos);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<BleDevice> CREATOR = new Creator<BleDevice>() {
+        @Override
+        public BleDevice createFromParcel(Parcel in) {
+            return new BleDevice(in);
+        }
+
+        @Override
+        public BleDevice[] newArray(int size) {
+            return new BleDevice[size];
+        }
+    };
+
+    public String getName() {
+        if (mDevice != null) {
+            return mDevice.getName();
+        }
+        return null;
+    }
+
+    public String getMac() {
+        if (mDevice != null) {
+            return mDevice.getAddress();
+        }
+        return null;
+    }
+
+    public String getKey() {
+        if (mDevice != null) {
+            return mDevice.getName() + mDevice.getAddress();
+        }
+        return "";
+    }
+
+    public BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    public void setDevice(BluetoothDevice device) {
+        this.mDevice = device;
+    }
+
+    public byte[] getScanRecord() {
+        return mScanRecord;
+    }
+
+    public void setScanRecord(byte[] scanRecord) {
+        this.mScanRecord = scanRecord;
+    }
+
+    public int getRssi() {
+        return mRssi;
+    }
+
+    public void setRssi(int rssi) {
+        this.mRssi = rssi;
+    }
+
+    public long getTimestampNanos() {
+        return mTimestampNanos;
+    }
+
+    public void setTimestampNanos(long timestampNanos) {
+        this.mTimestampNanos = timestampNanos;
+    }
+
+}

+ 60 - 0
FastBleLib/src/main/java/com/clj/fastble/data/BleMsg.java

@@ -0,0 +1,60 @@
+package com.clj.fastble.data;
+
+
+
+public class BleMsg {
+
+    // Scan
+    public static final int MSG_SCAN_DEVICE = 0X00;
+
+    // Connect
+    public static final int MSG_CONNECT_FAIL = 0x01;
+    public static final int MSG_DISCONNECTED = 0x02;
+    public static final int MSG_RECONNECT = 0x03;
+    public static final int MSG_DISCOVER_SERVICES = 0x04;
+    public static final int MSG_DISCOVER_FAIL = 0x05;
+    public static final int MSG_DISCOVER_SUCCESS = 0x06;
+    public static final int MSG_CONNECT_OVER_TIME = 0x07;
+
+    // Notify
+    public static final int MSG_CHA_NOTIFY_START = 0x11;
+    public static final int MSG_CHA_NOTIFY_RESULT = 0x12;
+    public static final int MSG_CHA_NOTIFY_DATA_CHANGE = 0x13;
+    public static final String KEY_NOTIFY_BUNDLE_STATUS = "notify_status";
+    public static final String KEY_NOTIFY_BUNDLE_VALUE = "notify_value";
+
+    // Indicate
+    public static final int MSG_CHA_INDICATE_START = 0x21;
+    public static final int MSG_CHA_INDICATE_RESULT = 0x22;
+    public static final int MSG_CHA_INDICATE_DATA_CHANGE = 0x23;
+    public static final String KEY_INDICATE_BUNDLE_STATUS = "indicate_status";
+    public static final String KEY_INDICATE_BUNDLE_VALUE = "indicate_value";
+
+    // Write
+    public static final int MSG_CHA_WRITE_START = 0x31;
+    public static final int MSG_CHA_WRITE_RESULT = 0x32;
+    public static final int MSG_SPLIT_WRITE_NEXT = 0x33;
+    public static final String KEY_WRITE_BUNDLE_STATUS = "write_status";
+    public static final String KEY_WRITE_BUNDLE_VALUE = "write_value";
+
+    // Read
+    public static final int MSG_CHA_READ_START = 0x41;
+    public static final int MSG_CHA_READ_RESULT = 0x42;
+    public static final String KEY_READ_BUNDLE_STATUS = "read_status";
+    public static final String KEY_READ_BUNDLE_VALUE = "read_value";
+
+    // Rssi
+    public static final int MSG_READ_RSSI_START = 0x51;
+    public static final int MSG_READ_RSSI_RESULT = 0x52;
+    public static final String KEY_READ_RSSI_BUNDLE_STATUS = "rssi_status";
+    public static final String KEY_READ_RSSI_BUNDLE_VALUE = "rssi_value";
+
+    // Mtu
+    public static final int MSG_SET_MTU_START = 0x61;
+    public static final int MSG_SET_MTU_RESULT = 0x62;
+    public static final String KEY_SET_MTU_BUNDLE_STATUS = "mtu_status";
+    public static final String KEY_SET_MTU_BUNDLE_VALUE = "mtu_value";
+
+
+
+}

+ 18 - 0
FastBleLib/src/main/java/com/clj/fastble/data/BleScanState.java

@@ -0,0 +1,18 @@
+package com.clj.fastble.data;
+
+
+public enum BleScanState {
+
+    STATE_IDLE(-1),
+    STATE_SCANNING(0X01);
+
+    private int code;
+
+    BleScanState(int code) {
+        this.code = code;
+    }
+
+    public int getCode() {
+        return code;
+    }
+}

+ 8 - 0
FastBleLib/src/main/java/com/clj/fastble/data/BleWriteState.java

@@ -0,0 +1,8 @@
+package com.clj.fastble.data;
+
+
+
+public class BleWriteState {
+
+    public static final int DATA_WRITE_SINGLE = 1;
+}

+ 47 - 0
FastBleLib/src/main/java/com/clj/fastble/exception/BleException.java

@@ -0,0 +1,47 @@
+package com.clj.fastble.exception;
+
+import java.io.Serializable;
+
+
+public abstract class BleException implements Serializable {
+
+    private static final long serialVersionUID = 8004414918500865564L;
+
+    public static final int ERROR_CODE_TIMEOUT = 100;
+    public static final int ERROR_CODE_GATT = 101;
+    public static final int ERROR_CODE_OTHER = 102;
+
+    private int code;
+    private String description;
+
+    public BleException(int code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public BleException setCode(int code) {
+        this.code = code;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public BleException setDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "BleException { " +
+               "code=" + code +
+               ", description='" + description + '\'' +
+               '}';
+    }
+}

+ 42 - 0
FastBleLib/src/main/java/com/clj/fastble/exception/ConnectException.java

@@ -0,0 +1,42 @@
+package com.clj.fastble.exception;
+
+import android.bluetooth.BluetoothGatt;
+
+
+public class ConnectException extends BleException {
+
+    private BluetoothGatt bluetoothGatt;
+    private int gattStatus;
+
+    public ConnectException(BluetoothGatt bluetoothGatt, int gattStatus) {
+        super(ERROR_CODE_GATT, "Gatt Exception Occurred! ");
+        this.bluetoothGatt = bluetoothGatt;
+        this.gattStatus = gattStatus;
+    }
+
+    public int getGattStatus() {
+        return gattStatus;
+    }
+
+    public ConnectException setGattStatus(int gattStatus) {
+        this.gattStatus = gattStatus;
+        return this;
+    }
+
+    public BluetoothGatt getBluetoothGatt() {
+        return bluetoothGatt;
+    }
+
+    public ConnectException setBluetoothGatt(BluetoothGatt bluetoothGatt) {
+        this.bluetoothGatt = bluetoothGatt;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "ConnectException{" +
+               "gattStatus=" + gattStatus +
+               ", bluetoothGatt=" + bluetoothGatt +
+               "} " + super.toString();
+    }
+}

+ 28 - 0
FastBleLib/src/main/java/com/clj/fastble/exception/GattException.java

@@ -0,0 +1,28 @@
+package com.clj.fastble.exception;
+
+
+public class GattException extends BleException {
+
+    private int gattStatus;
+
+    public GattException(int gattStatus) {
+        super(ERROR_CODE_GATT, "Gatt Exception Occurred! ");
+        this.gattStatus = gattStatus;
+    }
+
+    public int getGattStatus() {
+        return gattStatus;
+    }
+
+    public GattException setGattStatus(int gattStatus) {
+        this.gattStatus = gattStatus;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "GattException{" +
+               "gattStatus=" + gattStatus +
+               "} " + super.toString();
+    }
+}

+ 10 - 0
FastBleLib/src/main/java/com/clj/fastble/exception/OtherException.java

@@ -0,0 +1,10 @@
+package com.clj.fastble.exception;
+
+
+public class OtherException extends BleException {
+
+    public OtherException(String description) {
+        super(ERROR_CODE_OTHER, description);
+    }
+
+}

+ 10 - 0
FastBleLib/src/main/java/com/clj/fastble/exception/TimeoutException.java

@@ -0,0 +1,10 @@
+package com.clj.fastble.exception;
+
+
+public class TimeoutException extends BleException {
+
+    public TimeoutException() {
+        super(ERROR_CODE_TIMEOUT, "Timeout Exception Occurred!");
+    }
+
+}

+ 238 - 0
FastBleLib/src/main/java/com/clj/fastble/scan/BleScanPresenter.java

@@ -0,0 +1,238 @@
+package com.clj.fastble.scan;
+
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+
+import com.clj.fastble.callback.BleScanPresenterImp;
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.data.BleMsg;
+import com.clj.fastble.utils.BleLog;
+import com.clj.fastble.utils.HexUtil;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public abstract class BleScanPresenter implements BluetoothAdapter.LeScanCallback {
+
+    private String[] mDeviceNames;
+    private String mDeviceMac;
+    private boolean mFuzzy;
+    private boolean mCanRepeatFound;
+    private boolean mNeedConnect;
+    private long mScanTimeout;
+    private BleScanPresenterImp mBleScanPresenterImp;
+
+    private final List<BleDevice> mBleDeviceList = new ArrayList<>();
+
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private boolean mHandling;
+
+    private static final class ScanHandler extends Handler {
+
+        private final WeakReference<BleScanPresenter> mBleScanPresenter;
+
+        ScanHandler(Looper looper, BleScanPresenter bleScanPresenter) {
+            super(looper);
+            mBleScanPresenter = new WeakReference<>(bleScanPresenter);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            BleScanPresenter bleScanPresenter = mBleScanPresenter.get();
+            if (bleScanPresenter != null) {
+                if (msg.what == BleMsg.MSG_SCAN_DEVICE) {
+                    final BleDevice bleDevice = (BleDevice) msg.obj;
+                    if (bleDevice != null) {
+                        bleScanPresenter.handleResult(bleDevice);
+//                        BleLog.e("bleDevice:"+bleDevice.getMac());
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleResult(final BleDevice bleDevice) {
+        mMainHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                onLeScan(bleDevice);
+            }
+        });
+        checkDevice(bleDevice);
+    }
+
+    public void prepare(String[] names, String mac, boolean fuzzy, boolean needConnect,
+                        long timeOut, BleScanPresenterImp bleScanPresenterImp, boolean isCanRepeatFound) {
+        mDeviceNames = names;
+        mDeviceMac = mac;
+        mFuzzy = fuzzy;
+        mNeedConnect = needConnect;
+        mScanTimeout = timeOut;
+        mBleScanPresenterImp = bleScanPresenterImp;
+        mCanRepeatFound = isCanRepeatFound;
+
+        mHandlerThread = new HandlerThread(BleScanPresenter.class.getSimpleName());
+        mHandlerThread.start();
+        mHandler = new ScanHandler(mHandlerThread.getLooper(), this);
+        mHandling = true;
+    }
+
+    public boolean ismNeedConnect() {
+        return mNeedConnect;
+    }
+
+    public BleScanPresenterImp getBleScanPresenterImp() {
+        return mBleScanPresenterImp;
+    }
+
+    @Override
+    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
+        if (device == null)
+            return;
+
+        if (!mHandling)
+            return;
+
+        Message message = mHandler.obtainMessage();
+        message.what = BleMsg.MSG_SCAN_DEVICE;
+        message.obj = new BleDevice(device, rssi, scanRecord, System.currentTimeMillis());
+        mHandler.sendMessage(message);
+    }
+
+    private void checkDevice(BleDevice bleDevice) {
+        if (TextUtils.isEmpty(mDeviceMac) && (mDeviceNames == null || mDeviceNames.length < 1)) {
+            correctDeviceAndNextStep(bleDevice);
+            return;
+        }
+
+        if (!TextUtils.isEmpty(mDeviceMac)) {
+            if (!mDeviceMac.equalsIgnoreCase(bleDevice.getMac()))
+                return;
+        }
+
+        if (mDeviceNames != null && mDeviceNames.length > 0) {
+            AtomicBoolean equal = new AtomicBoolean(false);
+            for (String name : mDeviceNames) {
+                String remoteName = bleDevice.getName();
+                if (remoteName == null)
+                    remoteName = "";
+                if (mFuzzy ? remoteName.contains(name) : remoteName.equals(name)) {
+                    equal.set(true);
+                }
+            }
+            if (!equal.get()) {
+                return;
+            }
+        }
+
+        correctDeviceAndNextStep(bleDevice);
+    }
+
+
+    private void correctDeviceAndNextStep(final BleDevice bleDevice) {
+        if (mNeedConnect) {
+            BleLog.i("devices detected  ------"
+                    + "  name:" + bleDevice.getName()
+                    + "  mac:" + bleDevice.getMac()
+                    + "  Rssi:" + bleDevice.getRssi()
+                    + "  scanRecord:" + HexUtil.formatHexString(bleDevice.getScanRecord()));
+
+            mBleDeviceList.add(bleDevice);
+            mMainHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    BleScanner.getInstance().stopLeScan();
+                }
+            });
+
+        } else {
+            AtomicBoolean hasFound = new AtomicBoolean(false);
+            for (BleDevice result : mBleDeviceList) {
+                if (result.getDevice().equals(bleDevice.getDevice())) {
+                    hasFound.set(true);
+                }
+            }
+            if (!hasFound.get() || mCanRepeatFound) {
+                BleLog.i(mCanRepeatFound + " device detected  ------"
+                        + "  name: " + bleDevice.getName()
+                        + "  mac: " + bleDevice.getMac()
+                        + "  Rssi: " + bleDevice.getRssi()
+                        + "  scanRecord: " + HexUtil.formatHexString(bleDevice.getScanRecord(), true));
+
+                if (!hasFound.get()) {
+                    mBleDeviceList.add(bleDevice);
+                }
+                mMainHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onScanning(bleDevice);
+                    }
+                });
+            }
+        }
+    }
+
+    public final void notifyScanStarted(final boolean success) {
+        mBleDeviceList.clear();
+
+        removeHandlerMsg();
+
+        if (success && mScanTimeout > 0) {
+            mMainHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    BleScanner.getInstance().stopLeScan();
+                }
+            }, mScanTimeout);
+        }
+
+        mMainHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                onScanStarted(success);
+            }
+        });
+    }
+
+    public final void notifyScanStopped() {
+        mHandling = false;
+        if (mHandlerThread == null){
+            return;
+        }
+
+        mHandlerThread.quit();
+        removeHandlerMsg();
+        mMainHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                onScanFinished(mBleDeviceList);
+            }
+        });
+    }
+
+    public final void removeHandlerMsg() {
+        mMainHandler.removeCallbacksAndMessages(null);
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    public abstract void onScanStarted(boolean success);
+
+    public abstract void onLeScan(BleDevice bleDevice);
+
+    public abstract void onScanning(BleDevice bleDevice);
+
+    public abstract void onScanFinished(List<BleDevice> bleDeviceList);
+}

+ 109 - 0
FastBleLib/src/main/java/com/clj/fastble/scan/BleScanRuleConfig.java

@@ -0,0 +1,109 @@
+package com.clj.fastble.scan;
+
+
+import com.clj.fastble.BleManager;
+
+import java.util.UUID;
+
+public class BleScanRuleConfig {
+
+    private UUID[] mServiceUuids = null;
+    private String[] mDeviceNames = null;
+    private String mDeviceMac = null;
+    private boolean mAutoConnect = false;
+    private boolean mCanRepeatFound = false;
+    private boolean mFuzzy = false;
+    private long mScanTimeOut = BleManager.DEFAULT_SCAN_TIME;
+
+    public UUID[] getServiceUuids() {
+        return mServiceUuids;
+    }
+
+    public String[] getDeviceNames() {
+        return mDeviceNames;
+    }
+
+    public String getDeviceMac() {
+        return mDeviceMac;
+    }
+
+    public boolean isAutoConnect() {
+        return mAutoConnect;
+    }
+
+    public boolean isFuzzy() {
+        return mFuzzy;
+    }
+
+    public boolean isCanRepeatFound() {
+        return mCanRepeatFound;
+    }
+
+    public long getScanTimeOut() {
+        return mScanTimeOut;
+    }
+
+    public static class Builder {
+
+        private UUID[] mServiceUuids = null;
+        private String[] mDeviceNames = null;
+        private String mDeviceMac = null;
+        private boolean mAutoConnect = false;
+        private boolean mCanRepeatFound = false;
+        private boolean mFuzzy = false;
+        private long mTimeOut = BleManager.DEFAULT_SCAN_TIME;
+
+        public Builder setServiceUuids(UUID[] uuids) {
+            this.mServiceUuids = uuids;
+            return this;
+        }
+
+        public Builder setDeviceName(boolean fuzzy, String... name) {
+            this.mFuzzy = fuzzy;
+            this.mDeviceNames = name;
+            return this;
+        }
+
+        public Builder setDeviceMac(String mac) {
+            this.mDeviceMac = mac;
+            return this;
+        }
+
+        public Builder setAutoConnect(boolean autoConnect) {
+            this.mAutoConnect = autoConnect;
+            return this;
+        }
+
+        /**
+         * 可以重复发现设备
+         */
+        public Builder setCanRepeatFound(boolean isCanRepeat) {
+            this.mCanRepeatFound = isCanRepeat;
+            return this;
+        }
+
+        public Builder setScanTimeOut(long timeOut) {
+            this.mTimeOut = timeOut;
+            return this;
+        }
+
+        void applyConfig(BleScanRuleConfig config) {
+            config.mServiceUuids = this.mServiceUuids;
+            config.mDeviceNames = this.mDeviceNames;
+            config.mDeviceMac = this.mDeviceMac;
+            config.mAutoConnect = this.mAutoConnect;
+            config.mFuzzy = this.mFuzzy;
+            config.mScanTimeOut = this.mTimeOut;
+            config.mCanRepeatFound = this.mCanRepeatFound;
+        }
+
+        public BleScanRuleConfig build() {
+            BleScanRuleConfig config = new BleScanRuleConfig();
+            applyConfig(config);
+            return config;
+        }
+
+    }
+
+
+}

+ 139 - 0
FastBleLib/src/main/java/com/clj/fastble/scan/BleScanner.java

@@ -0,0 +1,139 @@
+package com.clj.fastble.scan;
+
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.clj.fastble.BleManager;
+import com.clj.fastble.callback.BleScanAndConnectCallback;
+import com.clj.fastble.callback.BleScanCallback;
+import com.clj.fastble.callback.BleScanPresenterImp;
+import com.clj.fastble.data.BleDevice;
+import com.clj.fastble.data.BleScanState;
+import com.clj.fastble.utils.BleLog;
+
+import java.util.List;
+import java.util.UUID;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class BleScanner {
+
+    public static BleScanner getInstance() {
+        return BleScannerHolder.sBleScanner;
+    }
+
+    private static class BleScannerHolder {
+        private static final BleScanner sBleScanner = new BleScanner();
+    }
+
+    private BleScanState mBleScanState = BleScanState.STATE_IDLE;
+
+    private final BleScanPresenter mBleScanPresenter = new BleScanPresenter() {
+
+        @Override
+        public void onScanStarted(boolean success) {
+            BleScanPresenterImp callback = mBleScanPresenter.getBleScanPresenterImp();
+            if (callback != null) {
+                callback.onScanStarted(success);
+            }
+        }
+
+        @Override
+        public void onLeScan(BleDevice bleDevice) {
+            if (mBleScanPresenter.ismNeedConnect()) {
+                BleScanAndConnectCallback callback = (BleScanAndConnectCallback)
+                        mBleScanPresenter.getBleScanPresenterImp();
+                if (callback != null) {
+                    callback.onLeScan(bleDevice);
+                }
+            } else {
+                BleScanCallback callback = (BleScanCallback) mBleScanPresenter.getBleScanPresenterImp();
+                if (callback != null) {
+                    callback.onLeScan(bleDevice);
+                }
+            }
+        }
+
+        @Override
+        public void onScanning(BleDevice result) {
+            BleScanPresenterImp callback = mBleScanPresenter.getBleScanPresenterImp();
+            if (callback != null) {
+                callback.onScanning(result);
+            }
+        }
+
+        @Override
+        public void onScanFinished(List<BleDevice> bleDeviceList) {
+            if (mBleScanPresenter.ismNeedConnect()) {
+                final BleScanAndConnectCallback callback = (BleScanAndConnectCallback)
+                        mBleScanPresenter.getBleScanPresenterImp();
+                if (bleDeviceList == null || bleDeviceList.size() < 1) {
+                    if (callback != null) {
+                        callback.onScanFinished(null);
+                    }
+                } else {
+                    if (callback != null) {
+                        callback.onScanFinished(bleDeviceList.get(0));
+                    }
+                    final List<BleDevice> list = bleDeviceList;
+                    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            BleManager.getInstance().connect(list.get(0), callback);
+                        }
+                    }, 100);
+                }
+            } else {
+                BleScanCallback callback = (BleScanCallback) mBleScanPresenter.getBleScanPresenterImp();
+                if (callback != null) {
+                    callback.onScanFinished(bleDeviceList);
+                }
+            }
+        }
+    };
+
+    public void scan(UUID[] serviceUuids, String[] names, String mac, boolean fuzzy,
+                     long timeOut, final BleScanCallback callback,boolean isCanRepeatFound) {
+
+        startLeScan(serviceUuids, names, mac, fuzzy, false, timeOut, callback,isCanRepeatFound);
+    }
+
+    public void scanAndConnect(UUID[] serviceUuids, String[] names, String mac, boolean fuzzy,
+                               long timeOut, BleScanAndConnectCallback callback) {
+
+        startLeScan(serviceUuids, names, mac, fuzzy, true, timeOut, callback,false);
+    }
+
+    private synchronized void startLeScan(UUID[] serviceUuids, String[] names, String mac, boolean fuzzy,
+                                          boolean needConnect, long timeOut, BleScanPresenterImp imp,boolean isCanRepeatFound) {
+
+        if (mBleScanState != BleScanState.STATE_IDLE) {
+            BleLog.w("scan action already exists, complete the previous scan action first");
+            if (imp != null) {
+                imp.onScanStarted(false);
+            }
+            return;
+        }
+
+        mBleScanPresenter.prepare(names, mac, fuzzy, needConnect, timeOut, imp,isCanRepeatFound);
+
+        boolean success = BleManager.getInstance().getBluetoothAdapter()
+                .startLeScan(serviceUuids, mBleScanPresenter);
+        mBleScanState = success ? BleScanState.STATE_SCANNING : BleScanState.STATE_IDLE;
+        mBleScanPresenter.notifyScanStarted(success);
+    }
+
+    public synchronized void stopLeScan() {
+        BleManager.getInstance().getBluetoothAdapter().stopLeScan(mBleScanPresenter);
+        mBleScanState = BleScanState.STATE_IDLE;
+        mBleScanPresenter.notifyScanStopped();
+    }
+
+    public BleScanState getScanState() {
+        return mBleScanState;
+    }
+
+
+}

+ 31 - 0
FastBleLib/src/main/java/com/clj/fastble/utils/BleLog.java

@@ -0,0 +1,31 @@
+package com.clj.fastble.utils;
+
+
+import android.util.Log;
+
+public final class BleLog {
+
+    public static boolean isPrint = true;
+    private static final String defaultTag = "FastBle";
+
+    public static void d(String msg) {
+        if (isPrint && msg != null)
+            Log.d(defaultTag, msg);
+    }
+
+    public static void i(String msg) {
+        if (isPrint && msg != null)
+            Log.i(defaultTag, msg);
+    }
+
+    public static void w(String msg) {
+        if (isPrint && msg != null)
+            Log.w(defaultTag, msg);
+    }
+
+    public static void e(String msg) {
+        if (isPrint && msg != null)
+            Log.e(defaultTag, msg);
+    }
+
+}

+ 34 - 0
FastBleLib/src/main/java/com/clj/fastble/utils/BleLruHashMap.java

@@ -0,0 +1,34 @@
+package com.clj.fastble.utils;
+
+
+import com.clj.fastble.bluetooth.BleBluetooth;
+
+import java.util.LinkedHashMap;
+
+public class BleLruHashMap<K, V> extends LinkedHashMap<K, V> {
+
+    private final int MAX_SIZE;
+
+    public BleLruHashMap(int saveSize) {
+        super((int) Math.ceil(saveSize / 0.75) + 1, 0.75f, true);
+        MAX_SIZE = saveSize;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
+        if (size() > MAX_SIZE && eldest.getValue() instanceof BleBluetooth) {
+            ((BleBluetooth) eldest.getValue()).disconnect();
+        }
+        return size() > MAX_SIZE;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (Entry<K, V> entry : entrySet()) {
+            sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
+        }
+        return sb.toString();
+    }
+
+}

+ 121 - 0
FastBleLib/src/main/java/com/clj/fastble/utils/HexUtil.java

@@ -0,0 +1,121 @@
+package com.clj.fastble.utils;
+
+public class HexUtil {
+
+    private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
+            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+    private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5',
+            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    public static char[] encodeHex(byte[] data) {
+        return encodeHex(data, true);
+    }
+
+    public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+    }
+
+    protected static char[] encodeHex(byte[] data, char[] toDigits) {
+        if (data == null)
+            return null;
+        int l = data.length;
+        char[] out = new char[l << 1];
+        for (int i = 0, j = 0; i < l; i++) {
+            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+            out[j++] = toDigits[0x0F & data[i]];
+        }
+        return out;
+    }
+
+
+    public static String encodeHexStr(byte[] data) {
+        return encodeHexStr(data, true);
+    }
+
+    public static String encodeHexStr(byte[] data, boolean toLowerCase) {
+        return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+    }
+
+
+    protected static String encodeHexStr(byte[] data, char[] toDigits) {
+        return new String(encodeHex(data, toDigits));
+    }
+
+    public static String formatHexString(byte[] data) {
+        return formatHexString(data, false);
+    }
+
+    public static String formatHexString(byte[] data, boolean addSpace) {
+        if (data == null || data.length < 1)
+            return null;
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < data.length; i++) {
+            String hex = Integer.toHexString(data[i] & 0xFF);
+            if (hex.length() == 1) {
+                hex = '0' + hex;
+            }
+            sb.append(hex);
+            if (addSpace)
+                sb.append(" ");
+        }
+        return sb.toString().trim();
+    }
+
+    public static byte[] decodeHex(char[] data) {
+        int len = data.length;
+
+        if ((len & 0x01) != 0) {
+            throw new RuntimeException("Odd number of characters.");
+        }
+
+        byte[] out = new byte[len >> 1];
+
+        // two characters form the hex value.
+        for (int i = 0, j = 0; j < len; i++) {
+            int f = toDigit(data[j], j) << 4;
+            j++;
+            f = f | toDigit(data[j], j);
+            j++;
+            out[i] = (byte) (f & 0xFF);
+        }
+
+        return out;
+    }
+
+
+    protected static int toDigit(char ch, int index) {
+        int digit = Character.digit(ch, 16);
+        if (digit == -1) {
+            throw new RuntimeException("Illegal hexadecimal character " + ch
+                    + " at index " + index);
+        }
+        return digit;
+    }
+
+
+    public static byte[] hexStringToBytes(String hexString) {
+        if (hexString == null || hexString.equals("")) {
+            return null;
+        }
+        hexString = hexString.trim();
+        hexString = hexString.toUpperCase();
+        int length = hexString.length() / 2;
+        char[] hexChars = hexString.toCharArray();
+        byte[] d = new byte[length];
+        for (int i = 0; i < length; i++) {
+            int pos = i * 2;
+            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+        }
+        return d;
+    }
+
+    public static byte charToByte(char c) {
+        return (byte) "0123456789ABCDEF".indexOf(c);
+    }
+
+    public static String extractData(byte[] data, int position) {
+        return HexUtil.formatHexString(new byte[]{data[position]});
+    }
+
+}

+ 200 - 0
README.md

@@ -0,0 +1,200 @@
+# ZFrame --Android快速开发集成框架
+
+## Summary
+ZFrame是Android端的快速开发框架,此项目主要对常用的数据库、图片加载、权限处理、Http请求、常用工具类进行
+统一封装,基本涵盖了除了UI以外的所有功能,易于上手,上手之后能够快速的完成项目框架搭建。
+此项目主要包含五大模块:Orm数据库、OkHttp封装、Glide图片加载、PermissionHelper权限处理、工具类集合.
+Orm数据库采用GreenDao, Http采用OkHttp, 图片加载采用Glide, 这三个库是经过从性能和易用度综合考虑确定的.封装的基本能满足平时的使用。
+如不满足可以自己进行扩展,或提交issue。
+
+
+## Feature
+1. Orm数据库:对GreenDao进行了封装,对常用的增删改查直接继承即可不用再写函数。
+1. Http请求:OkHttp进行了封装,主要目的就是可以异步回调时主线程中执行任务,其实这套封装是参考鸿洋大神的,进行了多处更改。感谢!
+1. 图片缓存:图片加载基本没做什么工作,但是考虑可能会换库,所以封装一层.
+1. Activity回调:封装ResultActivityHelper使用回调,免除startActivityForResult时需要在onActivityResult中写判断。
+1. 权限处理:封装6.0的权限管理控制,基于回调模式。
+1. 工具类集合:工具类是经过多个项目积累整理的,基本都会用到,涉及面比较广。
+
+## reference library version
+1. GeeenDao :3.3.0
+1. OkHtpp : 3.10.0
+1. Glide : 4.11.0
+1. Gson : 2.8.2
+
+## Gradle
+app的build.gradle中添加
+```
+dependencies {
+    implementation 'com.github.zcolin:ZFrame:latest.release'
+}
+```
+工程的build.gradle中添加
+```
+allprojects {
+	repositories {
+		...
+		maven { url 'https://jitpack.io' }
+	}
+}
+```
+
+## Usage
+#### HttpDemo
+Http的主要操作类为com.zcolin.frame.http.ZHttp, 内置了get post upload download 等多参数函数,其中使用最多的为通过报文
+协议直接将服务器返回的json报文解析成对象,进行回调,以下为此功能说明
+```
+//首先建一个返回对象的基类,此类实现ZReply接口,实现接口的三个函数,如下
+public class HttpBaseReplyBean implements ZReply {
+    public int    error; //协议中的字段,根据协议自定
+    public String status;//协议中的字段,根据协议自定
+
+    /**
+     * 判断返回的数据是否成功,具体规则由接口协议制定
+     */
+    @Override
+    public boolean isSuccess() {
+        return "success".equals(status);
+    }
+
+    /**
+     * 服务端返回的状态码
+     */
+    @Override
+    public int getReplyCode() {
+        return error;
+    }
+
+    /**
+     * 服务端返回的错误信息
+     */
+    @Override
+    public String getErrorMessage() {
+        return "";
+    }
+}
+
+//具体调用,ZResponse的构造第二和第三个参数分别为进度条的context和进度条信息,如果只填第一个参数,则不显示进度条
+ZHttp.get(HttpUrl.URL_BAIDU_TEST, new ZResponse<BaiduWeatherReply>(BaiduWeatherReply.class, mActivity, "正在获取数据……") {
+    @Override
+    public void onError(int code, String error) {
+        super.onError(code, error);
+        //TODO 错误处理,父类已默认调用了ToastUtil.toastShort(error);
+    }
+
+    @Override
+    public void onSuccess(Response response, BaiduWeatherReply resObj) {
+        if (resObj.results.size() > 0) {
+            BaiduWeatherReply.ResultsBean bean = resObj.results.get(0);
+            textView.setText("city:" + bean.currentCity + " pm25:" + bean.pm25);
+        }
+    }
+});
+
+// 是否去除空/null字段。 default true
+ZHtpp.setIsClearEmptyValue(boolean)
+ZHtpp.setIsClearNullValue(boolean)
+
+```
+#### DB 使用
+以下实体类的规则为GreenDao的官方规则,具体可以google
+```
+// 首先定义实体
+@Entity(nameInDb = "NOTE", generateConstructors = false)
+public class Employee {
+    @Id
+    public long id;
+
+    @NotNull
+    public String name;
+
+    public String group;
+
+    public String company;
+
+    public Date date;
+}
+
+//daomaster为build代码时自动生成
+Employee employee = new Employee();
+`
+`
+`
+DaoMaster daomaster = new DaoMaster(getDaoOpenHelper(BaseApp.APP_CONTEXT, "default").getWritableDatabase());
+DaoHelper<DaoSession> daoHelper = new DaoHelper(daomaster.newSession());   
+boolean b = daoHelper.insertOrReplaceObject(employee);
+
+针对DaoMaster已经封装了一个辅助类,具体见app中的com.zcolin.frame.demo.db.DaoManager
+```
+#### Activity的回调处理
+```
+//继承BaseFrameActivity和 BaseFrameFrag的可以直接调用   
+startActivityWithCallback(intent, new ResultActivityHelper.ResultActivityListener() {
+    @Override
+    public void onResult(int requestCode, int resultCode, Intent data) {
+	if (resultCode == RESULT_OK) {
+	    userDetail.nick_name = data.getStringExtra("data");
+	    initData();
+	}
+    }
+});
+```
+
+#### 权限回调
+```   
+//默认常用的权限(包括摄像头、设备信息、SD卡、联系人、拨打电话权限)
+PermissionHelper.requestCameraPermission(mActivity, new PermissionsResultAction() {
+    @Override
+    public void onGranted() {
+        
+    }
+
+    @Override
+    public void onDenied(String permission) {
+
+    }
+});
+
+//默认权限中没有的,根据需要自己直接申请
+PermissionHelper.requestPermission(this, new String[]{Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE}, new PermissionsResultAction() {
+    @Override
+    public void onGranted() {
+
+    }
+
+    @Override
+    public void onDenied(String permission) {
+
+    }
+});
+
+```
+#### 常用工具类介绍与使用 在Frame框架Utils包中,可以查看源码或者查看生成的JavaDoc。 常用包括:
+* ActivityUtil :Activity 相关操作 工具类。
+* AppUtil:APP管理工具类,如获取应用相关信息,应用退出 重启 安装 卸载.判断程序运行状况等函数的定义。
+* ArrayUtils: 数组操作工具类。
+* BitmapUtil: Bitmap工具类,如图片缩放,保存图片,复制图片,圆角处理等。
+* CalendarUtil:日期操作工具类,主要格式化时间。
+* DeviceUtil:设备操作工具类,如获取设备识别码,获取设置屏保时间,获取设置字体缩放大小等。
+* DisplayUtil:像素转换工具类,如px互转dp,获取设备density等。
+* FastClickUtils:防止控件被重复点击的辅助类。
+* FileOpenUtil:使用第三方程序打开文件操作工具类。
+* FileUtil:文件操作工具类,如文件拷贝,文件读写操作。
+* GsonUtil:使用gson对Json数据和对象互转的工具类。
+* KeyBoardUtil:软键盘操作如弹出、关闭工具类。
+* LogUtil:日志操作工具类。
+* MD5Util&RSAUtils:加密解密工具类。
+* RegexUtil:正则工具类,匹配常用的如邮箱,手机号等。
+* ScreenUtil:屏幕工具类,如获取屏幕宽高,获取截图等。
+* SDCardUtil:SD卡工具类,如获取sd卡是否可用,获取sd可用容量等。
+* SpannableStringBuilderUtil:SpannableStringBuilder的工具类。
+* SpannableStringUtil:SpannableString的工具类。
+* SPUtil:SharedPreferences配置文件读写封装。
+* StateListUtil:StateList的构建类。
+* StringFormatUtil:字符串格式化工具类。
+* StringUtil:String相关操作工具类。
+* SystemDownloadApk:托管系统下载工具类。
+* SystemIntentUtil:调用系统Intent工具类。
+* ToastUtil:Toast工具类,可以在子线程调用。
+
+## MIT

+ 1 - 0
_config.yml

@@ -0,0 +1 @@
+theme: jekyll-theme-cayman

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
app/agconnect-services.json


+ 165 - 0
app/build.gradle

@@ -0,0 +1,165 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'org.greenrobot.greendao'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+
+    //GreenDao配置
+    greendao {
+        schemaVersion 1
+        //daoPackage 'com.greendao.gen'  //dao包名
+        //targetGenDir 'src/main/java'  //生成目录
+    }
+
+    //lint配置
+    lintOptions {
+        checkReleaseBuilds false
+        abortOnError false
+    }
+
+    defaultConfig {
+        applicationId "com.rdiot.yx485"
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
+
+        // dex突破65535的限制
+        //multiDexEnabled true
+        manifestPlaceholders = [
+                //从 3.1.2.0 版本开始,APPID 占位符从 GETUI_APP_ID 切换为 GETUI_APPID
+                //后续所有产品的 APPID 均统一配置为 GETUI_APPID 占位符
+                GETUI_APPID    : "LSFQhLICT5AiOMB7CIZ1D9",
+                // 华为 相关应用参数
+                HUAWEI_APP_ID  : "107630077",
+
+                // 小米相关应用参数
+                XIAOMI_APP_ID  : "2882303761520219041",
+                XIAOMI_APP_KEY : "5372021977041",
+
+                // OPPO 相关应用参数
+                OPPO_APP_KEY   : "d5862f83314e40ad84ec2bb6bae9b78f",
+                OPPO_APP_SECRET: "c98c7a7869a6436e894c484160cc51a9",
+
+                // VIVO 相关应用参数
+                VIVO_APP_ID    : "105883724",
+                VIVO_APP_KEY   : "8d267be2db14231854fec1b5e5ea4e54",
+
+                // 魅族相关应用参数
+                MEIZU_APP_ID   : "155304",
+                MEIZU_APP_KEY  : "3e45010653eb4ad9afeea39bbfa2dd72",
+
+                // 荣耀相关应用参数
+                HONOR_APP_ID   : "900841768",
+        ]
+
+        ndk {
+            abiFilters 'arm64-v8a', 'armeabi-v7a'//, 'armeabi' // 添加arm64以适配android14系统手机vivo y200i
+        }
+    }
+    signingConfigs {
+        release {
+            storeFile file('yx485.jks')
+            storePassword "yx485123456"
+            keyAlias 'yx485'
+            keyPassword "yx485123456"
+        }
+    }
+    buildTypes {
+        debug {
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            signingConfig signingConfigs.release
+        }
+
+        release {
+            minifyEnabled false
+            zipAlignEnabled false
+            shrinkResources false
+            signingConfig signingConfigs.release
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    ndkVersion "22.1.7171670"
+
+    buildFeatures {
+        viewBinding true
+        dataBinding false // 显式禁用 DataBinding(关键步骤!)
+    }
+
+    dataBinding {
+        enabled true
+    }
+}
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+    implementation fileTree(include: ['*.jar'], dir: 'libs')
+    implementation project(':libFrame')
+    implementation project(':FastBleLib')
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'com.google.android.material:material:1.4.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+    implementation 'org.greenrobot:greendao:3.3.0'
+    implementation 'org.webrtc:google-webrtc:1.0.32006'
+    //底部按钮
+    implementation 'com.yun.ospl:jtabstrip:0.1.13-Y'
+    implementation 'com.github.zcolin:ZRecyclerView:3.0.0'
+    implementation 'com.github.pinguo-zhouwei:MZBannerView:v2.0.2'
+    implementation 'com.contrarywind:Android-PickerView:4.1.9'
+    implementation 'com.guolindev.permissionx:permissionx:1.7.1'
+    implementation 'com.github.lindroy:iOSDialog:1.0.0'
+    implementation 'com.google.android.gms:play-services-location:18.0.0'
+    //ZXingLite https://github.com/jenly1314/ZXingLite
+    implementation 'com.github.jenly1314:zxing-lite:2.2.1'
+//    implementation 'com.tencent:mmkv:1.2.14'
+    // Kotlin 协程核心库
+    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"
+    // 用于 Android 的协程扩展库
+    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
+    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" // JSON序列化库
+
+    // ViewModel
+    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
+    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
+    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1"
+
+    implementation "androidx.activity:activity-ktx:1.5.1"
+    implementation "androidx.fragment:fragment-ktx:1.3.0"
+    //Serialize https://github.com/liangjingkanji/Serialize
+    implementation 'com.github.liangjingkanji:Serialize:1.3.2'
+    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
+    implementation 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.1'
+
+    implementation 'io.github.lucksiege:pictureselector:v3.11.1'
+    implementation 'io.github.lucksiege:compress:v3.11.1'
+
+    implementation 'com.getui:gtsdk:3.3.10.0'  //个推SDK
+    implementation 'com.getui:gtc:3.2.18.0'  //个推核心组件
+    // 根据所需厂商选择集成
+    implementation 'com.getui.opt:hwp:3.1.2'   // 华为
+    implementation 'com.huawei.hms:push:6.12.0.300'
+    implementation 'com.getui.opt:xmp:3.3.3'   // 小米
+    implementation 'com.assist-v3:oppo:3.5.0'  // oppo
+    implementation 'com.google.code.gson:gson:2.6.2'
+    implementation 'commons-codec:commons-codec:1.6'
+    implementation 'com.android.support:support-annotations:28.0.0'
+    implementation 'com.assist-v3:vivo:3.2.0'  // vivo
+    implementation 'com.getui.opt:mzp:3.2.4'   // 魅族
+    implementation 'com.getui.opt:honor:3.6.0' // 荣耀
+    implementation 'com.hihonor.mcs:push:7.0.61.303'
+
+// https://mvnrepository.com/artifact/com.github.tbruyelle/rxpermissions
+    implementation("com.github.tbruyelle:rxpermissions:2.x.v0.9.3")
+    // For developers using AndroidX in their applications
+    implementation 'pub.devrel:easypermissions:3.0.0'
+//    implementation("com.huawei.hms:scanplus:2.1.0.300")
+}
+
+

+ 17 - 0
app/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\as_android_sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 20 - 0
app/release/output.json

@@ -0,0 +1,20 @@
+{
+  "version": 1,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.rdiot.yx485",
+  "variantName": "release",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "properties": [],
+      "versionCode": 10,
+      "versionName": "10",
+      "enabled": true,
+      "outputFile": "app-release.apk"
+    }
+  ]
+}

+ 195 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.rdiot.yx485">
+    <!-- 访问网络,网络定位需要上网 -->
+    <uses-permission android:name="android.permission.INTERNET" /> <!-- 操作摄像头 -->
+    <uses-permission android:name="android.permission.CAMERA" /> <!-- 拨打电话 -->
+    <uses-permission android:name="android.permission.CALL_PHONE" /> <!-- 写SD卡 -->
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <!-- 允许获取网络状态,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选 -->
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 允许获取wifi网络信息,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选 -->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 允许获取wifi状态改变,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选 -->
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 允许写设备缓存,用于问题排查 -->
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission
+        android:name="android.permission.BLUETOOTH"
+        android:maxSdkVersion="30" />
+    <uses-permission
+        android:name="android.permission.BLUETOOTH_ADMIN"
+        android:maxSdkVersion="30" /> <!-- Android12 的蓝牙权限 如果您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备) -->
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- Android12 的蓝牙权限 如果您的应用使当前设备可被其他蓝牙设备检测到 -->
+    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <!-- Android12 的蓝牙权限 如果您的应用与已配对的蓝牙设备通信或者获取当前手机蓝牙是否打开 -->
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission
+        android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" />
+
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+    <uses-permission android:name="android.permission.MANAGE_MEDIA" />
+
+    <!--适配Android R包可见性 开始-->
+    <queries package="${applicationId}">
+        <intent>
+            <action android:name="android.media.action.IMAGE_CAPTURE">
+
+            </action>
+        </intent>
+        <intent>
+            <action android:name="android.media.action.ACTION_VIDEO_CAPTURE">
+
+            </action>
+        </intent>
+        <intent>
+            <action android:name="com.getui.sdk.action" />
+        </intent>
+    </queries>
+    <!--适配Android R包可见性 结束-->
+
+    <application
+        android:name=".app.App"
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:screenOrientation="portrait"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name=".device.view.BindDeviceActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".login.SplashActivity"
+            android:exported="true"
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".message.MessageContentListActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".MainActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".HttpDemoActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".PermissionAndActivityResultActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".home.HomeCreateActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".home.HomeActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".message.MessageListActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".CheckUpdateActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".InviteActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".PerInfoActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".login.SetPwdActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".login.LoginsmsActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".login.LoginWithPwdActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".room.RoomSetDeviceActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.DeviceManageActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.DeviceListActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.ScanDeviceActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.ResetActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.EnterRoomInfoActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.view.EnterWifiInfoActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".main.MainActivityTab"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".QRCodeActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".webview.WebViewActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".room.RoomManageActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".device.ConsumableActivity"
+            android:exported="false"
+            android:screenOrientation="portrait" />
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="com.rdiot.yx485.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
+        <meta-data
+            android:name="com.amap.api.v2.apikey"
+            android:value="eb0f9dc9689fa516b4caec2f4f15550b" />
+
+        <service
+            android:name=".push.GTPushService"
+            android:exported="false"
+            android:label="PushService"
+            android:process=":pushservice" />
+        <service android:name=".push.GTPushIntentService" />
+    </application>
+
+</manifest>

+ 26 - 0
app/src/main/assets/index.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Android-H5 Interaction</title>
+</head>
+<body>
+<h1>H5 and Android Interaction</h1>
+<button onclick="getToken()">Call Android getToken</button>
+<button onclick="closeH5()">Call Android closeH5</button>
+<p id="result"></p>
+
+<script>
+    function getToken() {
+        // 调用 Android 的方法并获取返回值
+        const token = window.Android.getToken();
+        document.getElementById("result").innerText = "token from Android: " + token;
+    }
+    function closeH5() {
+        // 调用 Android 的方法关闭当前页面
+        window.Android.closeH5();
+    }
+</script>
+</body>
+</html>

+ 92 - 0
app/src/main/java/com/rdiot/yx485/BaseActivity.java

@@ -0,0 +1,92 @@
+/*
+ * *********************************************************
+ *   author   colin
+ *   email    wanglin2046@126.com
+ *   date     18-1-9 上午9:59
+ * ********************************************************
+ */
+
+package com.rdiot.yx485;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import com.king.zxing.util.LogUtils;
+import com.rdiot.yx485.util.IntentUtils;
+import com.zcolin.frame.app.BaseFrameActivity;
+import com.zcolin.frame.app.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * DBDemo
+ */
+@SuppressLint("Registered")
+public class BaseActivity extends BaseFrameActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // 设置沉浸式状态栏
+        Window window = getWindow();
+        List<String> activities = new ArrayList<>();
+        activities.add("SplashActivity");
+        activities.add("MainActivityTab");
+        LogUtils.d(getClass().getSimpleName());
+
+        if (activities.contains(getClass().getSimpleName())) {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 清除半透明状态栏标志
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // 添加绘制系统栏背景标志
+            window.setStatusBarColor(Color.TRANSPARENT); // 设置状态栏颜色为透明
+
+            // 如果需要内容延伸到状态栏后面(沉浸式效果)
+            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        } else if (getClass().getSimpleName().equals("LoginWithPwdActivity")) {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 清除半透明状态栏标志
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // 添加绘制系统栏背景标志
+            window.setStatusBarColor(Color.TRANSPARENT); // 设置状态栏颜色为透明
+
+            // 如果需要内容延伸到状态栏后面(沉浸式效果)
+            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        } else {
+            getWindow().setStatusBarColor(Color.WHITE);
+        }
+    }
+
+    public void showMessage(String message) {
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onMessageEvent(MessageEvent event) {
+        LogUtils.d(event.message);
+        /* 处理事件 */
+        if ("reLogin".equals(event.message)) {
+            IntentUtils.intentToLogin(this);
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        EventBus.getDefault().register(this);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        EventBus.getDefault().unregister(this);
+    }
+}

+ 142 - 0
app/src/main/java/com/rdiot/yx485/CheckUpdateActivity.java

@@ -0,0 +1,142 @@
+package com.rdiot.yx485;
+
+import static com.rdiot.yx485.UpdateMgr.GET_UNKNOWN_APP_SOURCES;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.webkit.CookieManager;
+
+import androidx.lifecycle.ViewModelProvider;
+
+import com.rdiot.yx485.databinding.ActivityCheckUpdateBinding;
+import com.rdiot.yx485.main.MainViewModel;
+import com.zcolin.frame.util.FileUtil;
+
+import java.io.File;
+
+public class CheckUpdateActivity extends BaseActivity {
+    private ActivityCheckUpdateBinding mBinding;
+    private MainViewModel mViewModel;
+    private String updataurl;
+    private String updateData;
+    private boolean isForceUpdate;
+
+    // 辅助方法:递归删除目录下的所有文件
+    private static boolean deleteDir(File dir) {
+        if (dir != null && dir.isDirectory()) {
+            String[] children = dir.list();
+            for (String aChildren : children) {
+                boolean success = deleteDir(new File(dir, aChildren));
+                if (!success) {
+                    return false;
+                }
+            }
+        }
+        return dir.delete();
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinding = ActivityCheckUpdateBinding.inflate(getLayoutInflater());
+        setContentView(mBinding.getRoot());
+
+        mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
+        initView();
+        observeViewModel();
+    }
+
+    private void observeViewModel() {
+        mViewModel.getNeedUpdate().observe(this, needUpdate -> {
+            if (!needUpdate) {
+                showMessage("当前已是最新版本");
+            }
+        });
+
+        mViewModel.getUpdateInfo().observe(this, updateInfo -> {
+            if (updateInfo != null) {
+                updataurl = updateInfo.download_url;
+                updateData = updateInfo.content;
+                isForceUpdate = updateInfo.force_update;
+
+                String content = updateInfo.content;
+                if (content == null || content.isEmpty()) {
+                    content = "发现新版本 " + updateInfo.version + "\n是否更新?";
+                }
+
+                showUpdateDialog(content, isForceUpdate);
+            }
+        });
+
+        mViewModel.getErrorMessage().observe(this, this::showMessage);
+    }
+
+    private void initView() {
+        mBinding.layoutTitle.tvTitle.setText("系统设置");
+        mBinding.layoutTitle.btnTitleLeft.setOnClickListener(v -> finish());
+        mBinding.update.setOnClickListener(view -> mViewModel.checkUpdate());
+        mBinding.btnClearCache.setOnClickListener(v -> clearCache());
+    }
+
+    private void clearCache() {
+        // 清除内存中的缓存(仅清除页面加载数据)
+        mBinding.webView.clearCache(true);
+
+        // 清除所有持久化的存储,包括文件系统和数据库
+        getApplicationContext().deleteDatabase("webview.db");
+        getApplicationContext().deleteDatabase("webviewCache.db");
+
+        // 对于API 21及以上,还需要考虑清除Service Worker缓存
+        CookieManager.getInstance().removeAllCookies(null);
+        CookieManager.getInstance().flush();
+
+        // 清除应用内部存储空间的缓存目录
+        clearAppCache();
+        showMessage("清除缓存成功");
+    }
+
+    public void clearAppCache() {
+        try {
+            File dir = getCacheDir(); // 获取应用内部缓存目录
+            if (dir != null && dir.isDirectory()) {
+                deleteDir(dir); // 使用上面定义的辅助方法删除目录及其内容
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 更新弹窗
+     */
+    private void showUpdateDialog(String content, boolean forceUpdate) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle("系统更新")
+                .setMessage(content)
+                .setPositiveButton("更新", (dialog, which) -> {
+                    String url = getFilesDir() + "/wuheng.apk";
+                    File file = new File(url);
+                    FileUtil.delete(file);
+                    UpdateMgr.checkPermission(this, updataurl);
+                });
+
+        if (!forceUpdate) {
+            builder.setNegativeButton("暂不更新", null);
+        }
+
+        AlertDialog alertDialog = builder.create();
+        alertDialog.setCancelable(!forceUpdate);
+        alertDialog.show();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (resultCode == RESULT_OK && requestCode == GET_UNKNOWN_APP_SOURCES) {
+            showMessage("安装应用");
+            showUpdateDialog(updateData, isForceUpdate);
+        }
+    }
+}

+ 194 - 0
app/src/main/java/com/rdiot/yx485/HttpDemoActivity.java

@@ -0,0 +1,194 @@
+/*
+ * *********************************************************
+ *   author   colin
+ *   email    wanglin2046@126.com
+ *   date     18-1-9 上午9:59
+ * ********************************************************
+ */
+
+package com.rdiot.yx485;
+
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.rdiot.yx485.http.entity.BaiduWeatherReply;
+import com.rdiot.yx485.http.entity.HttpBaseReplyBean;
+import com.rdiot.yx485.http.entity.HttpCommonReply;
+import com.zcolin.frame.app.BaseApp;
+import com.zcolin.frame.demo.http.HttpUrl;
+import com.zcolin.frame.http.ZHttp;
+import com.zcolin.frame.http.ZReply;
+import com.zcolin.frame.http.response.ZResponse;
+import com.zcolin.frame.http.response.ZStringResponse;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+
+/**
+ * HttpDemo
+ */
+public class HttpDemoActivity extends BaseActivity implements View.OnClickListener {
+    private LinearLayout      llContent;
+    private TextView          textView;
+    private ArrayList<Button> listButton = new ArrayList<>();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_http_db);
+
+        init();
+    }
+
+    private void init() {
+        llContent = getView(R.id.ll_content);
+        textView = getView(R.id.textview);
+        textView.setMovementMethod(new ScrollingMovementMethod());
+        listButton.add(addButton("获取百度数据"));
+        listButton.add(addButton("获取对象"));
+        listButton.add(addButton("Post数据(请看代码,无效果)"));
+        listButton.add(addButton("上传文件(请看代码,无效果)"));
+
+        for (Button btn : listButton) {
+            btn.setOnClickListener(this);
+        }
+    }
+
+    private Button addButton(String text) {
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                                                         ViewGroup.LayoutParams.WRAP_CONTENT);
+        Button button = new Button(mActivity);
+        button.setText(text);
+        button.setGravity(Gravity.CENTER);
+        button.setAllCaps(false);
+        llContent.addView(button, params);
+        return button;
+    }
+
+    /**
+     * 获取为经解析的字符串
+     * <p>
+     * 此处为Demo,实际使用一般使用{@link #getObject()}
+     */
+    public void getBaiduStringData() {
+        ZHttp.get(HttpUrl.URL_BAIDU_TEST, new ZStringResponse(mActivity, "正在获取数据") {
+            @Override
+            public void onError(int code, Call call, Exception e) {
+                //Toast.makeText(BaseApp.APP_CONTEXT, LogUtil.ExceptionToString(e), Toast.LENGTH_SHORT).show();
+            }
+
+            @Override
+            public void onSuccess(Response response, String resObj) {
+                textView.setText(resObj);
+            }
+        });
+    }
+    /**
+     * 获取为经解析的字符串
+     * <p>
+     * 此处为Demo,实际使用一般使用{@link #getObject()}
+     */
+    public void login() {
+        ZHttp.postJson(HttpUrl.LOGIN, "{\"username\": \"admin\",\"password\": \"admin123\"}", new ZResponse<HttpBaseReplyBean>() {
+            @Override
+            public void onSuccess(Response response, HttpBaseReplyBean resObj) {
+                Toast.makeText(BaseApp.APP_CONTEXT, "code"+resObj.code, Toast.LENGTH_SHORT).show();
+            }
+
+            @Override
+            public void onError(int code, String error) {
+                super.onError(code, error);
+            }
+        });
+
+    }
+
+
+    /**
+     * 获取对象
+     * 实际使用时一般使用此方法,而不是直接解析字符窜
+     * <p>
+     * ZResponse的参数,第二个参数是显示旋转进度条则传入,第三个参数是进度条文字,如果不需要进度条则只需要传入第一个参数
+     */
+    public void getObject() {
+        ZHttp.get(HttpUrl.URL_BAIDU_TEST, new ZResponse<BaiduWeatherReply>(mActivity, "正在获取数据……") {
+            @Override
+            public void onError(int code, String error) {
+                //Toast.makeText(BaseApp.APP_CONTEXT,error, Toast.LENGTH_SHORT).show();
+            }
+
+            @Override
+            public void onSuccess(Response response, BaiduWeatherReply resObj) {
+                if (resObj.results.size() > 0) {
+                    BaiduWeatherReply.ResultsBean bean = resObj.results.get(0);
+                    textView.setText("city:" + bean.currentCity + " pm25:" + bean.pm25);
+                }
+            }
+        });
+    }
+
+    /**
+     * POST请求, 返回Gson序列化的对象
+     * <p>
+     * 带有progressBar
+     */
+    public void postWithBarResponse() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("param1", "sss");
+        ZHttp.post(HttpUrl.URL_BAIDU_TEST, params, new ZResponse<HttpCommonReply>(mActivity, "正在加载……") {
+            @Override
+            public void onSuccess(Response response, HttpCommonReply httpBaseBean) {
+                textView.setText(String.valueOf(httpBaseBean));
+            }
+
+            @Override
+            public void onError(int code, String error) {
+                textView.setText(error);
+            }
+        });
+    }
+
+    /**
+     * 上传文件,可以和其他参数一起上传,也可以单独上传
+     */
+    public void uploadFile() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("param1", "sss");
+        HashMap<String, File> fileParams = new HashMap<>();
+        fileParams.put("key", new File(""));
+        ZHttp.uploadFile(HttpUrl.URL_BAIDU_TEST,
+                         params,
+                         fileParams,
+                         new ZResponse<HttpCommonReply>(mActivity, "正在加载……") {
+                             @Override
+                             public void onSuccess(Response response, HttpCommonReply httpBaseBean) {
+                             }
+
+                             @Override
+                             public void onError(int code, String error) {
+                             }
+                         });
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == listButton.get(0)) {
+           // getBaiduStringData();
+            login();
+        } else if (v == listButton.get(1)) {
+            getObject();
+        }
+    }
+}

+ 78 - 0
app/src/main/java/com/rdiot/yx485/InviteActivity.java

@@ -0,0 +1,78 @@
+package com.rdiot.yx485;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.lifecycle.ViewModelProvider;
+
+import com.rdiot.yx485.bean.HomeBase;
+import com.rdiot.yx485.bean.HomeResultBean;
+import com.rdiot.yx485.databinding.ActivityInviteBinding;
+import com.rdiot.yx485.home.HomeViewModel;
+
+public class InviteActivity extends BaseActivity {
+    private ActivityInviteBinding mBinding;
+    private HomeViewModel mViewModel;
+
+    public static Intent newIntent(Context context, HomeResultBean.HomeInfo info) {
+        Intent intent = new Intent();
+        intent.setClass(context, InviteActivity.class);
+        intent.putExtra("homeid", info.record_id);
+        intent.putExtra("home_name", info.name);
+        return intent;
+    }
+
+    public static void newIntent(Context context, HomeBase info) {
+        Intent intent = new Intent();
+        intent.setClass(context, InviteActivity.class);
+        intent.putExtra("homeid", info.getId());
+        intent.putExtra("home_name", info.getName());
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinding = ActivityInviteBinding.inflate(getLayoutInflater());
+        setContentView(mBinding.getRoot());
+
+        mViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
+        initView();
+        observeViewModel();
+    }
+
+    private void initView() {
+        Intent intent = getIntent();
+        String home_id = intent.getStringExtra("homeid");
+        String home_name = intent.getStringExtra("home_name");
+        if (!TextUtils.isEmpty(home_name)) {
+            mBinding.homename.setText("邀请家人加入" + home_name);
+        }
+        mBinding.layoutTitle.tvTitle.setText("邀请家人");
+        mBinding.layoutTitle.btnTitleLeft.setOnClickListener(view -> onBackPressed());
+        mBinding.btnInvite.setOnClickListener(view -> {
+            String phone = mBinding.etPhone.getText().toString();
+            if (TextUtils.isEmpty(phone)) {
+                showMessage("请输入手机号");
+                return;
+            }
+            mViewModel.inviteMember(phone, home_id);
+        });
+    }
+
+    private void observeViewModel() {
+        mViewModel.getInviteResult().observe(this, success -> {
+            if (success) {
+                showMessage("邀请成功");
+                Intent intent = new Intent();
+                intent.putExtra("changed", true);
+                setResult(RESULT_OK, intent);
+                onBackPressed();
+            }
+        });
+
+        mViewModel.getErrorMessage().observe(this, this::showMessage);
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 49 - 0
app/src/main/java/com/rdiot/yx485/MainActivity.java


+ 245 - 0
app/src/main/java/com/rdiot/yx485/PerInfoActivity.java

@@ -0,0 +1,245 @@
+package com.rdiot.yx485;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.bumptech.glide.Glide;
+import com.luck.picture.lib.basic.PictureSelector;
+import com.luck.picture.lib.config.SelectMimeType;
+import com.luck.picture.lib.engine.CompressFileEngine;
+import com.luck.picture.lib.entity.LocalMedia;
+import com.luck.picture.lib.interfaces.OnResultCallbackListener;
+import com.luck.picture.lib.utils.SandboxTransformUtils;
+import com.rdiot.yx485.bean.PersonBean;
+import com.rdiot.yx485.databinding.ActivityPerInfoBinding;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.main.MainViewModel;
+import com.rdiot.yx485.util.DialogUtils;
+import com.rdiot.yx485.util.GlideEngine;
+import com.rdiot.yx485.util.IntentUtils;
+import com.rdiot.yx485.util.LocalCache;
+import com.rdiot.yx485.util.PermissionUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import top.zibin.luban.Luban;
+import top.zibin.luban.OnNewCompressListener;
+
+public class PerInfoActivity extends BaseActivity {
+    private final MainViewModel mViewModel = new MainViewModel();
+    private ActivityPerInfoBinding mBinding;
+    private String mImageUrl;
+    private String mUserName;
+    private boolean mChanged = false;
+    private PersonBean.UserInfo mUserInfo;
+
+    public static void newIntent(Context context) {
+        Intent intent = new Intent(context, PerInfoActivity.class);
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinding = ActivityPerInfoBinding.inflate(getLayoutInflater());
+        setContentView(mBinding.getRoot());
+
+        initData();
+        initView();
+    }
+
+    private void initData() {
+        mUserInfo = LocalCache.getUserInfo();
+        if (mUserInfo == null) {
+            onBackPressed();
+            return;
+        }
+        mUserName = mUserInfo.user_name;
+
+        mViewModel.getErrorMessage().observe(this, this::showMessage);
+        mViewModel.getUploadInfo().observe(this, uploadInfo -> {
+            if (uploadInfo == null) {
+                return;
+            }
+
+            mImageUrl = uploadInfo.getUrl();
+            mChanged = true;
+            refreshView();
+        });
+        mViewModel.getUpdateState().observe(this, success -> {
+            if (success) {
+                mChanged = false;
+                onBackPressed();
+            }
+        });
+        mViewModel.getDeleteAccountState().observe(this, success -> {
+            if (success) {
+                // 账号注销成功,返回登录页面
+                IntentUtils.intentToLogin(this);
+//                Intent intent = new Intent(this, LoginWithPwdActivity.class);
+//                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+//                startActivity(intent);
+//                finish();
+            }
+        });
+
+        refreshView();
+    }
+
+    private void initView() {
+        mBinding.layoutTitle.tvTitle.setText("个人资料");
+        mBinding.layoutTitle.btnTitleLeft.setOnClickListener(v -> onBackPressed());
+
+        mBinding.headimage.setOnClickListener(v -> {
+            if (PermissionUtils.INSTANCE.checkPermissionGranted(this, "")) {
+                selectPicture();
+            }
+        });
+        mBinding.username.setOnClickListener(v -> DialogUtils.showInputDialog(this, "修改昵称", mUserName, "请输入昵称", name ->
+        {
+            mChanged = true;
+            mUserName = name;
+            refreshView();
+        }));
+        mBinding.btnSave.setOnClickListener(v -> {
+            if (mUserInfo == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(mUserName)) {
+                mUserInfo.user_name = mUserName;
+            }
+
+            if (!TextUtils.isEmpty(mImageUrl)) {
+                mUserInfo.photo = mImageUrl;
+            }
+
+            mViewModel.updateUserInfo(mUserInfo);
+        });
+
+        // 修改密码点击事件
+        mBinding.layoutResetPassword.setOnClickListener(v -> {
+            // 跳转到修改密码页面
+            IntentUtils.intentToSetPwd(this, false);
+        });
+
+        // 注销账号点击事件
+        mBinding.layoutDeleteAccount.setOnClickListener(v -> {
+            DialogUtils.showAlertDialog(this, "注销账号", "注销账号后,您的所有数据将被删除且无法恢复。确定要注销账号吗?",
+                    (dialog, which) -> {
+                        // 确认注销账号
+                        mViewModel.deleteAccount();
+                    },
+                    (dialog, which) -> {
+                        // 取消操作
+                        dialog.dismiss();
+                    });
+        });
+    }
+
+    private void selectPicture() {
+        PictureSelector.create(this)
+                .openGallery(SelectMimeType.ofImage())
+                .isDisplayCamera(false)
+                .setImageEngine(GlideEngine.createGlideEngine())
+                .setMaxSelectNum(1)
+                .setSandboxFileEngine((context, srcPath, mineType, call) -> {
+                    if (call != null) {
+                        String sandboxPath = SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType);
+                        call.onCallback(srcPath, sandboxPath);
+                    }
+                })
+                .setCompressEngine((CompressFileEngine) (context, source, call) ->
+                        Luban.with(context).load(source).ignoreBy(500)
+                                .setCompressListener(new OnNewCompressListener() {
+                                    @Override
+                                    public void onStart() {
+
+                                    }
+
+                                    @Override
+                                    public void onSuccess(String source, File compressFile) {
+                                        if (call != null) {
+                                            call.onCallback(source, compressFile.getAbsolutePath());
+                                        }
+                                    }
+
+                                    @Override
+                                    public void onError(String source, Throwable e) {
+                                        if (call != null) {
+                                            call.onCallback(source, null);
+                                        }
+                                    }
+                                }).launch())
+                .forResult(new OnResultCallbackListener<LocalMedia>() {
+                    @SuppressLint("NotifyDataSetChanged")
+                    @Override
+                    public void onResult(ArrayList<LocalMedia> result) {
+                        if (result.isEmpty()) {
+                            return;
+                        }
+
+                        mViewModel.uploadFile(result.get(0).getAvailablePath());
+                    }
+
+                    @Override
+                    public void onCancel() {
+                    }
+                });
+    }
+
+    private void refreshView() {
+        showUserName();
+        showHeaderView();
+        mBinding.btnSave.setVisibility(mChanged ? VISIBLE : GONE);
+    }
+
+    private void showUserName() {
+        if (!TextUtils.isEmpty(mUserName)) {
+            mBinding.username.setText(mUserName);
+            return;
+        }
+
+        if (mUserInfo == null || TextUtils.isEmpty(mUserInfo.user_name)) {
+            return;
+        }
+
+        mBinding.username.setText(mUserInfo.user_name);
+    }
+
+    private void showHeaderView() {
+        if (!TextUtils.isEmpty(mImageUrl)) {
+            Glide.with(this)
+                    .load(Url.BASE_URL + mImageUrl)
+                    .error(Glide.with(this).load(R.drawable.tx))
+                    .into(mBinding.headimage);
+            return;
+        }
+
+        if (mUserInfo == null || TextUtils.isEmpty(mUserInfo.photo)) {
+            return;
+        }
+
+        Glide.with(this)
+                .load(Url.BASE_URL + mUserInfo.photo)
+                .error(Glide.with(this).load(R.drawable.tx))
+                .into(mBinding.headimage);
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mChanged) {
+            DialogUtils.showAlertDialog(this, "温馨提示", "您的资料已经修改,请点击保存提交修改。", (dialog, which) -> {
+            }, (dialog, which) -> super.onBackPressed());
+            return;
+        }
+        super.onBackPressed();
+    }
+}

+ 39 - 0
app/src/main/java/com/rdiot/yx485/PermissionAndActivityResultActivity.java

@@ -0,0 +1,39 @@
+/*
+ * *********************************************************
+ *   author   colin
+ *   email    wanglin2046@126.com
+ *   date     18-1-9 上午9:59
+ * ********************************************************
+ */
+
+package com.rdiot.yx485;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+
+
+/**
+ * DBDemo
+ */
+public class PermissionAndActivityResultActivity extends BaseActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_permission_activityresult);
+
+        final EditText editText = getView(R.id.edittext);
+        Button btn = getView(R.id.button);
+        if (mBundle != null) {
+            editText.setText(mBundle.getString("data"));
+        }
+
+
+        btn.setOnClickListener(v -> {
+            mActivity.setResult(RESULT_OK, new Intent().putExtra("data", editText.getText().toString()));
+            mActivity.finish();
+        });
+    }
+}

+ 57 - 0
app/src/main/java/com/rdiot/yx485/QRCodeActivity.kt

@@ -0,0 +1,57 @@
+package com.rdiot.yx485
+
+import android.os.Bundle
+import android.widget.ImageButton
+import com.king.zxing.CaptureActivity
+import com.king.zxing.DecodeConfig
+import com.king.zxing.DecodeFormatManager
+import com.king.zxing.analyze.MultiFormatAnalyzer
+
+/**
+ * 扫码
+ * @author mR2hao
+ * @date 2022/11/25
+ */
+class QRCodeActivity : CaptureActivity() {
+    override fun getLayoutId() = R.layout.act_qr_code
+
+    override fun initCameraScan() {
+        super.initCameraScan()
+
+
+        //初始化解码配置
+        val decodeConfig = DecodeConfig().apply {
+            //如果只有识别二维码的需求,这样设置效率会更高,不设置默认为DecodeFormatManager.DEFAULT_HINTS
+            hints = DecodeFormatManager.QR_CODE_HINTS
+            //设置是否全区域识别,默认false
+            isFullAreaScan = false
+            //设置识别区域比例,默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
+            areaRectRatio = 0.8f
+            //设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数
+            setAreaRectVerticalOffset(0).areaRectHorizontalOffset = 0
+        }
+
+
+        //在启动预览之前,设置分析器,只识别二维码
+        cameraScan
+            .setVibrate(true) //设置是否震动,默认为false
+            .setNeedAutoZoom(true) //二维码太小时可自动缩放,默认为false
+            .setAnalyzer(MultiFormatAnalyzer(decodeConfig)) //设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现
+
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
+
+        findViewById<ImageButton>(R.id.ib_left).setOnClickListener {
+            finish()
+        }
+    }
+
+    override fun finish() {
+        super.finish()
+        overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
+    }
+}

+ 62 - 0
app/src/main/java/com/rdiot/yx485/UpdateBean.java

@@ -0,0 +1,62 @@
+package com.rdiot.yx485;
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+public class UpdateBean extends BaseReplyBean {
+
+
+    /**
+     * code : string
+     * data : {"need_update":true,"update_info":{"creator":"string","download_url":"string","created_at":"string","version":"string","content":"string","force_update":0,"platform":"string"}}
+     */
+
+    public DataBean data;
+
+    public static class DataBean {
+        /**
+         * need_update : true
+         * update_info : {"creator":"string","download_url":"string","created_at":"string","version":"string","content":"string","force_update":0,"platform":"string"}
+         */
+
+        public boolean need_update;
+        public UpdateInfo update_info;
+
+        public static class UpdateInfo {
+            /**
+             * creator : string
+             * download_url : string
+             * created_at : string
+             * version : string
+             * content : string
+             * force_update : 0
+             * platform : string
+             */
+
+            public String record_id;
+            public String version;
+            public String content;
+            public boolean force_update;
+            public String download_url;
+            public String platform;
+            public String creator;
+            public String created_at;
+        }
+
+        @Override
+        public String toString() {
+            return "DataBean{" +
+                    "need_update=" + need_update +
+                    ", update_info=" + update_info +
+                    '}';
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "UpdateBean{" +
+                "data=" + data +
+                ", code=" + code +
+                ", message='" + message + '\'' +
+                '}';
+    }
+}

+ 100 - 0
app/src/main/java/com/rdiot/yx485/UpdateMgr.java

@@ -0,0 +1,100 @@
+package com.rdiot.yx485;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import androidx.core.content.FileProvider;
+
+import com.zcolin.frame.http.ZHttp;
+import com.zcolin.frame.http.response.ZFileResponse;
+
+import java.io.File;
+
+import okhttp3.Call;
+import okhttp3.Response;
+
+public class UpdateMgr {
+    public static final int GET_UNKNOWN_APP_SOURCES = 150;
+    /**
+     * 下载App
+     */
+    public static void downLoadApp(final Activity activity, String url) {
+        String fileName = "zonghe.apk";
+        if (fileName != null) {
+            ZHttp.downLoadFile(url, new ZFileResponse( activity.getFilesDir()+ "/zonghe.apk", activity, "正在下载……") {
+                @Override
+                public void onError(int code, Call call, Exception e) {
+                    Toast.makeText(activity, "下载失败!", Toast.LENGTH_SHORT).show();
+                }
+
+                @Override
+                public void onSuccess(Response response, File resObj) {
+//                    AppUtil.installBySys(App.APP_CONTEXT, resObj);
+                    installDownloadApp(activity, resObj);
+                }
+
+                @Override
+                public void onProgress(float progress, long total) {
+                    super.onProgress(progress, total);
+                    setBarMsg("正在下载……");
+                  //  setBarMsg("正在下载……" + (int)(progress * 100) + "/" + 100);
+                }
+            });
+        } else {
+            Toast.makeText(activity, "下载URL不合法", Toast.LENGTH_SHORT).show();
+        }
+    }
+    private static void installDownloadApp(Context mContext, File apkfile){
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            Uri apkUri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", apkfile);
+            Intent install = new Intent(Intent.ACTION_VIEW);
+            install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            install.setDataAndType(apkUri, "application/vnd.android.package-archive");
+            mContext.startActivity(install);
+        } else {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
+                    "application/vnd.android.package-archive");
+            mContext.startActivity(i);
+        }
+    }
+
+    private static String getFileNameByUrl(String url) {
+        String fileName = null;
+        if (!TextUtils.isEmpty(url)) {
+            try {
+                int start = url.lastIndexOf(".com/") + 5;
+                int end =  url.lastIndexOf("?");
+                fileName = url.substring(start,end);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return fileName;
+    }
+    public static void checkPermission(Activity mContext, String url) {
+//        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
+//            boolean hasInstallPermission = mContext.getPackageManager().canRequestPackageInstalls();
+//            if (hasInstallPermission) {
+//                //下载安装应用
+//                downLoadApp(mContext, url);
+//            } else {
+//                //跳转至"安装未知应用"权限界面,引导用户开启权限
+//                Uri selfPackageUri = Uri.parse("package:" + mContext.getPackageName());
+//                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
+//                mContext.startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);
+//            }
+//        } else {
+            //下载安装应用
+            downLoadApp(mContext, url);
+//        }
+    }
+}

+ 12 - 0
app/src/main/java/com/rdiot/yx485/UpdateRequestBean.java

@@ -0,0 +1,12 @@
+package com.rdiot.yx485;
+
+public class UpdateRequestBean {
+
+    /**
+     * username : admin
+     * password : admin123
+     */
+
+    public String version;
+    public String platform;
+}

+ 95 - 0
app/src/main/java/com/rdiot/yx485/adapter/ContentAdapter.java

@@ -0,0 +1,95 @@
+package com.rdiot.yx485.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.DeviceTreeBean;
+
+import java.util.List;
+
+public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
+
+    private List<DeviceTreeBean.DataBean> mData;
+    private OnItemClickListener listener;
+    private Context mContext;
+    private int mSelectedIndex = 0;
+
+    public ContentAdapter(List<DeviceTreeBean.DataBean> data, OnItemClickListener listener) {
+        this.mData = data;
+        this.listener = listener;
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    public void setSelected(int position) {
+        if (mData != null && mData.size() > position) {
+            mSelectedIndex = position;
+        }
+        notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        mContext = parent.getContext();
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_add_list_content, parent, false);
+        return new ViewHolder(view);
+    }
+
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        if (mData != null && !TextUtils.isEmpty(mData.get(position).name)) {
+            if (position == mSelectedIndex) {
+                holder.title.setVisibility(View.VISIBLE);
+                holder.itemView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.white));
+                holder.text.setTextColor(holder.text.getContext().getResources().getColor(R.color.title_333333));
+            } else {
+                holder.title.setVisibility(View.GONE);
+                holder.itemView.setBackgroundColor(Color.parseColor("#F9F9F9"));
+                holder.text.setTextColor(holder.text.getContext().getResources().getColor(R.color.title_999999));
+            }
+            holder.text.setText(mData.get(position).name);
+            holder.itemView.setOnClickListener(v -> {
+                if (listener != null) {
+                    listener.onItemClick(position);
+                }
+            });
+        }
+
+    }
+
+    @Override
+    public int getItemCount() {
+        if (mData == null) {
+            return 0;
+        }
+        return mData.size();
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(int position);
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+
+        TextView text;
+        TextView title;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            text = itemView.findViewById(R.id.text);
+            title = itemView.findViewById(R.id.title);
+        }
+    }
+}

+ 57 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceIconListAdapter.kt

@@ -0,0 +1,57 @@
+package com.rdiot.yx485.adapter
+
+import android.content.Context
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.rdiot.yx485.R
+import com.rdiot.yx485.adapter.DeviceIconListAdapter.DeviceIconView
+import com.rdiot.yx485.bean.ModelRepleBean.DataBean.DeviceInfosBean
+import com.rdiot.yx485.databinding.ItemDeviceIconBinding
+import com.rdiot.yx485.http.Url
+
+class DeviceIconListAdapter(val list: List<DeviceInfosBean>?, context: Context) :
+    RecyclerView.Adapter<DeviceIconView>() {
+
+    private var mContext = context
+    private var mList: List<DeviceInfosBean>? = list
+
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): DeviceIconView {
+        val binding = ItemDeviceIconBinding.inflate(LayoutInflater.from(mContext), parent, false)
+        return DeviceIconView(binding)
+    }
+
+    override fun onBindViewHolder(
+        holder: DeviceIconView,
+        position: Int
+    ) {
+        holder.setIsRecyclable(false)
+        holder.bindView(position)
+    }
+
+    override fun getItemCount(): Int {
+        return mList?.size ?: 0
+    }
+
+    inner class DeviceIconView(val binding: ItemDeviceIconBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        fun bindView(position: Int) {
+            val device = mList?.get(position)
+            device?.let {
+                if (!TextUtils.isEmpty(it.device_icon)) {
+                    Glide.with(mContext)
+                        .load(Url.BASE_URL + it.device_icon)
+                        .error(Glide.with(mContext).load(R.drawable.find_d))
+                        .into(binding.ivDeviceIcon)
+                }
+            }
+
+        }
+    }
+}

+ 89 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceListAdapter.java

@@ -0,0 +1,89 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.EditDeviceReplyBean.DataBean.DeviceBean;
+import com.rdiot.yx485.databinding.DeviceItemListContentBinding;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.webview.WebViewActivity;
+
+import java.util.List;
+
+public class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> {
+    private final Context mContext;
+    public List<DeviceBean> mList;
+
+    private OnClickDeleteListener mDeleteListener;
+
+    public DeviceListAdapter(Context context, List<DeviceBean> list) {
+        mContext = context;
+        mList = list;
+    }
+
+    public void setDeleteListener(OnClickDeleteListener deleteListener) {
+        mDeleteListener = deleteListener;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        DeviceItemListContentBinding binding = DeviceItemListContentBinding.inflate(LayoutInflater.from(mContext), parent, false);
+        return new ViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindData(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mList == null ? 0 : mList.size();
+    }
+
+    public interface OnClickDeleteListener {
+        void onClickDelete(String deviceId);
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        private final DeviceItemListContentBinding mBinding;
+
+        ViewHolder(DeviceItemListContentBinding itemView) {
+            super(itemView.getRoot());
+            mBinding = itemView;
+        }
+
+        void bindData(int position) {
+            DeviceBean device = mList.get(position);
+            if (!TextUtils.isEmpty(device.name)) {
+                mBinding.name.setText(device.name);
+            }
+            mBinding.stae.setText(device.is_master ? "设备在线" : "设备离线");
+            if (!TextUtils.isEmpty(device.icon)) {
+                Glide.with(mContext)
+                        .load(Url.BASE_URL + device.icon)
+                        .error(Glide.with(itemView.getContext()).load(R.drawable.find_d))
+                        .into(mBinding.image);
+            }
+            mBinding.deleteButton.setOnClickListener(v -> {
+                if (mDeleteListener != null) {
+                    mDeleteListener.onClickDelete(device.record_id);
+                }
+            });
+            mBinding.layoutContainer.setOnClickListener(v -> {
+                if (!TextUtils.isEmpty(device.detail_url)) {
+                    WebViewActivity.newIntent(mContext, device.detail_url, null);
+                }
+            });
+        }
+    }
+}

+ 86 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceRoomAdapter.java

@@ -0,0 +1,86 @@
+package com.rdiot.yx485.adapter;
+
+import android.annotation.SuppressLint;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.DeviceRoomListBean;
+
+import java.util.List;
+
+public class DeviceRoomAdapter extends RecyclerView.Adapter<DeviceRoomAdapter.ViewHolder> {
+
+    public List<DeviceRoomListBean.RoomBean> mList;
+    public int mSelectedPosition = -1;//初始化为负值,表示默认不选中任何选项
+
+    public DeviceRoomAdapter(List<DeviceRoomListBean.RoomBean> data) {
+        mList = data;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_room_content, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindData(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mList == null ? 0 : mList.size();
+    }
+
+    public DeviceRoomListBean.RoomBean getSelectedRoom() {
+        if (mList.isEmpty() || mSelectedPosition == -1 || mList.size() <= mSelectedPosition) {
+            return null;
+        }
+
+        return mList.get(mSelectedPosition);
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        private final TextView name;
+        private final CheckBox checkBox;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            name = itemView.findViewById(R.id.name);
+            checkBox = itemView.findViewById(R.id.checkBox);
+        }
+
+        @SuppressLint("NotifyDataSetChanged")
+        public void bindData(int position) {
+            DeviceRoomListBean.RoomBean roomBean = mList.get(position);
+            if (roomBean == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(roomBean.name)) {
+                name.setText(roomBean.name);
+            }
+
+            checkBox.setChecked(position == mSelectedPosition);
+            checkBox.setOnCheckedChangeListener((compoundButton, b) -> {
+                if (b) {
+                    mSelectedPosition = position;
+                } else {
+                    mSelectedPosition = -1;
+                }
+                notifyDataSetChanged();
+            });
+        }
+    }
+}

+ 49 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceSpinnerAdapter.java

@@ -0,0 +1,49 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.rdiot.yx485.bean.EditDeviceReplyBean;
+
+import java.util.List;
+
+public class DeviceSpinnerAdapter extends BaseAdapter {
+    private final Context context;
+    private final List<EditDeviceReplyBean.DataBean.DeviceBean> items;
+
+    public DeviceSpinnerAdapter(Context context, List<EditDeviceReplyBean.DataBean.DeviceBean> items) {
+        this.context = context;
+        this.items = items;
+    }
+
+    @Override
+    public int getCount() {
+        return items.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return items.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        if (convertView == null) {
+            convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_spinner_dropdown_item, parent, false);
+        }
+
+        TextView text = convertView.findViewById(android.R.id.text1);
+        text.setText(items.get(position).name);
+
+        return convertView;
+    }
+}

+ 80 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceTypeContentAdapter.kt

@@ -0,0 +1,80 @@
+package com.rdiot.yx485.adapter
+
+import android.content.Context
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.rdiot.yx485.adapter.DeviceTypeContentAdapter.DeviceTypeListView
+import com.rdiot.yx485.bean.DeviceTreeBean
+import com.rdiot.yx485.bean.DeviceTreeBean.DataBean.ChildrenBean
+import com.rdiot.yx485.databinding.ItemDeviceTypeContentBinding
+import com.rdiot.yx485.device.view.ResetActivity
+
+class DeviceTypeContentAdapter(val list: List<DeviceTreeBean.DataBean>?, context: Context) :
+    RecyclerView.Adapter<DeviceTypeListView>() {
+
+    private var mContext = context
+    private var mList: List<DeviceTreeBean.DataBean>? = list
+
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): DeviceTypeListView {
+        val binding =
+            ItemDeviceTypeContentBinding.inflate(LayoutInflater.from(mContext), parent, false)
+        return DeviceTypeListView(binding)
+    }
+
+    override fun onBindViewHolder(
+        holder: DeviceTypeListView,
+        position: Int
+    ) {
+        holder.setIsRecyclable(false)
+        holder.bindView(position)
+    }
+
+    override fun getItemCount(): Int {
+        return mList?.size ?: 0
+    }
+
+    inner class DeviceTypeListView(val binding: ItemDeviceTypeContentBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        fun bindView(position: Int) {
+            val device = mList?.get(position)
+            device?.let {
+                if (!TextUtils.isEmpty(it.name)) {
+                    binding.tvSubtitle.text = it.name
+                }
+
+                val adapter = DeviceTypeListAdapter(
+                    it.children,
+                    object : DeviceTypeListAdapter.OnItemClickListener {
+                        override fun onItemClick(item: ChildrenBean?) {
+                            item?.let {
+                                gotoDeviceBind(it)
+                            }
+                        }
+                    })
+                binding.rvDeviceTypeList.layoutManager = GridLayoutManager(mContext, 3)
+                binding.rvDeviceTypeList.adapter = adapter
+            }
+
+        }
+    }
+
+
+    private fun gotoDeviceBind(item: ChildrenBean) {
+        val bundle = Bundle()
+        bundle.putBoolean("isMaster", item.is_master)
+        bundle.putBoolean("bluetooth", item.bluetooth)
+        bundle.putBoolean("need_device_code", item.need_device_code)
+        bundle.putString("device_type_id", item.record_id)
+        bundle.putString("blue_code", item.blue_code)
+        bundle.putString("device_name", item.name)
+        ResetActivity.newIntent(mContext, bundle, item.steps)
+    }
+}

+ 86 - 0
app/src/main/java/com/rdiot/yx485/adapter/DeviceTypeListAdapter.java

@@ -0,0 +1,86 @@
+package com.rdiot.yx485.adapter;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.DeviceTreeBean;
+import com.rdiot.yx485.http.Url;
+
+import java.util.List;
+
+public class DeviceTypeListAdapter extends RecyclerView.Adapter<DeviceTypeListAdapter.ViewHolder> {
+
+    private final List<DeviceTreeBean.DataBean.ChildrenBean> mData;
+    private final OnItemClickListener mListener;
+
+    public DeviceTypeListAdapter(List<DeviceTreeBean.DataBean.ChildrenBean> data, OnItemClickListener listener) {
+        mData = data;
+        mListener = listener;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_item_list, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindData(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mData == null ? 0 : mData.size();
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(DeviceTreeBean.DataBean.ChildrenBean item);
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        private final ImageView icon;
+        private final TextView text;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            icon = itemView.findViewById(R.id.icon);
+            text = itemView.findViewById(R.id.text);
+        }
+
+        public void bindData(int position) {
+            DeviceTreeBean.DataBean.ChildrenBean bean = mData.get(position);
+            if (bean == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(bean.name)) {
+                text.setText(bean.name);
+            }
+
+            if (bean.icon != null) {
+                Glide.with(icon.getContext())
+                        .load(Url.BASE_URL + bean.icon)
+                        .error(Glide.with(icon.getContext()).load(R.drawable.find_d))
+                        .into(icon);
+            }
+
+            itemView.setOnClickListener(v -> {
+                if (mListener != null) {
+                    mListener.onItemClick(bean);
+                }
+            });
+        }
+    }
+}

+ 119 - 0
app/src/main/java/com/rdiot/yx485/adapter/ExampleListAdapter.java

@@ -0,0 +1,119 @@
+package com.rdiot.yx485.adapter;
+
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.king.zxing.util.LogUtils;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.ModelRepleBean;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.webview.WebViewActivity;
+import com.zcolin.gui.zrecyclerview.BaseRecyclerAdapter;
+
+
+public class ExampleListAdapter extends BaseRecyclerAdapter<ModelRepleBean.DataBean> {
+
+    private final Context mContext;
+    private final OnItemClickListener mClickListener;
+    private boolean mShowAction = false;
+
+    public ExampleListAdapter(Context context, OnItemClickListener listener, boolean showAction) {
+        mContext = context;
+        mClickListener = listener;
+        mShowAction = showAction;
+    }
+
+    @Override
+    public int getItemLayoutId(int viewType) {
+        return R.layout.view_example_item;
+    }
+
+    @Override
+    public void setUpData(CommonHolder holder, int position, int viewType, ModelRepleBean.DataBean data) {
+        holder.setIsRecyclable(false);
+
+        TextView name = getView(holder, R.id.name);
+        TextView desc = getView(holder, R.id.tv_desc);
+        ImageView imageView = getView(holder, R.id.image);
+        ImageView smallIcon = getView(holder, R.id.iv_small_icon);
+        ImageView arrowRight = getView(holder, R.id.iv_arrow_right);
+        RecyclerView rvDeviceListView = getView(holder, R.id.rv_device_list);
+        rvDeviceListView.setLayoutManager(new LinearLayoutManager(mContext));
+
+        ToggleButton toggle = getView(holder, R.id.btn_toggle);
+        toggle.setVisibility(mShowAction ? GONE : VISIBLE);
+        TextView action = getView(holder, R.id.btn_action);
+        action.setVisibility(mShowAction ? VISIBLE : GONE);
+
+        if (!TextUtils.isEmpty(data.icon)) {
+            Glide.with(holder.itemView.getContext())
+                    .load(Url.BASE_URL + data.icon)
+                    .error(Glide.with(holder.itemView.getContext()).load(R.drawable.find_d))
+                    .into(imageView);
+            // holder.deviceIcon.setImageResource(device.icon);
+        }
+
+        if (!TextUtils.isEmpty(data.name)) {
+            name.setText(data.name);
+        }
+
+        if (!TextUtils.isEmpty(data.small_icon)) {
+            Glide.with(holder.itemView.getContext())
+                    .load(Url.BASE_URL + data.small_icon)
+                    .error(Glide.with(holder.itemView.getContext()).load(R.drawable.find_d))
+                    .into(smallIcon);
+        }
+
+        toggle.setChecked(data.status);
+        toggle.setOnCheckedChangeListener((compoundButton, b) -> {
+            LogUtils.d("checked:" + b);
+            if (mClickListener != null) {
+                mClickListener.onItemClick(data.record_id);
+            }
+        });
+        action.setOnClickListener(v -> {
+            if (mClickListener != null) {
+                mClickListener.onItemClick(data.record_id);
+            }
+        });
+
+        if (!TextUtils.isEmpty(data.detail_url)) {
+            holder.itemView.setOnClickListener(v -> WebViewActivity.newIntent(mContext, data.detail_url, null));
+        }
+
+        if (data.device_infos == null) {
+            desc.setText("相关设备已离线");
+            smallIcon.setVisibility(GONE);
+            arrowRight.setVisibility(GONE);
+        } else {
+            for (ModelRepleBean.DataBean.DeviceInfosBean infosBean : data.device_infos) {
+                if (infosBean.online) {
+                    continue;
+                }
+
+                desc.setText("相关设备已离线");
+                smallIcon.setVisibility(GONE);
+                arrowRight.setVisibility(GONE);
+                return;
+            }
+
+            DeviceIconListAdapter adapter = new DeviceIconListAdapter(data.device_infos, mContext);
+            rvDeviceListView.setAdapter(adapter);
+        }
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(String id);
+    }
+}

+ 79 - 0
app/src/main/java/com/rdiot/yx485/adapter/HomeAddRoomtAdapter.java

@@ -0,0 +1,79 @@
+package com.rdiot.yx485.adapter;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.DefHomeRoomBean;
+
+import java.util.List;
+
+public class HomeAddRoomtAdapter extends RecyclerView.Adapter<HomeAddRoomtAdapter.RoomViewHolder> {
+
+    private List<DefHomeRoomBean.DataBean.CityInfo> roomList;
+    private OnItemClickListener listener;
+    public HomeAddRoomtAdapter(List<DefHomeRoomBean.DataBean.CityInfo> roomList, OnItemClickListener mlistener) {
+        this.roomList = roomList;
+        listener=mlistener;
+    }
+
+    @NonNull
+    @Override
+    public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.home_room_add, parent, false);
+        return new RoomViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RoomViewHolder holder, int position) {
+        DefHomeRoomBean.DataBean.CityInfo room = roomList.get(position);
+        if(!TextUtils.isEmpty(room.name)){
+            holder.roomName.setText(room.name);
+
+        }
+        holder.room_icon.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (listener != null) {
+                    listener.onItemClick(position);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        return roomList.size();
+    }
+
+    public List<DefHomeRoomBean.DataBean.CityInfo> getdate() {
+        return roomList;
+    }
+    public void setdate(List<DefHomeRoomBean.DataBean.CityInfo> list) {
+         roomList=list;
+    }
+
+    static class RoomViewHolder extends RecyclerView.ViewHolder {
+        TextView roomName;
+        TextView deviceCount;
+        ImageView room_icon;
+
+        RoomViewHolder(View itemView) {
+            super(itemView);
+            roomName = itemView.findViewById(R.id.room_name);
+            room_icon = itemView.findViewById(R.id.room_icon);
+            deviceCount = itemView.findViewById(R.id.device_count);
+        }
+    }
+    public interface OnItemClickListener {
+        void onItemClick(int position);
+    }
+}

+ 68 - 0
app/src/main/java/com/rdiot/yx485/adapter/HomeListAdapter.java

@@ -0,0 +1,68 @@
+package com.rdiot.yx485.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.HomeListBean;
+
+import java.util.List;
+
+public class HomeListAdapter extends RecyclerView.Adapter<HomeListAdapter.ViewHolder> {
+
+    private final List<HomeListBean.DataBean> mData;
+    private final OnItemClickListener listener;
+
+    public HomeListAdapter(List<HomeListBean.DataBean> data, OnItemClickListener listener) {
+        this.mData = data;
+        this.listener = listener;
+    }
+
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_item_homelist_content, parent, false);
+        return new ViewHolder(view);
+    }
+
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        if (mData != null && !mData.isEmpty()) {
+            holder.text.setText(mData.get(position).name);
+        }
+
+        holder.itemView.setOnClickListener(v -> {
+            if (listener != null) {
+                listener.onItemClick(position);
+            }
+        });
+
+    }
+
+    @Override
+    public int getItemCount() {
+        return mData.size();
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+
+        TextView text;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            text = itemView.findViewById(R.id.name);
+        }
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(int position);
+    }
+}

+ 32 - 0
app/src/main/java/com/rdiot/yx485/adapter/HomePagerAdapter.java

@@ -0,0 +1,32 @@
+package com.rdiot.yx485.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import com.rdiot.yx485.bean.HomeResultBean;
+import com.rdiot.yx485.home.HomeFragment;
+
+import java.util.List;
+
+public class HomePagerAdapter extends FragmentStateAdapter {
+    private List<HomeResultBean.HomeInfo> mlist;
+
+    public HomePagerAdapter(@NonNull FragmentActivity fm, List<HomeResultBean.HomeInfo> list) {
+        super(fm);
+        this.mlist = list;
+    }
+
+    @NonNull
+    @Override
+    public Fragment createFragment(int position) {
+        // 根据位置创建不同的Fragment
+        return HomeFragment.newInstance(mlist.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mlist == null ? 0 : mlist.size();
+    }
+}

+ 101 - 0
app/src/main/java/com/rdiot/yx485/adapter/HomePersonAdapter.java

@@ -0,0 +1,101 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.HomeResultBean.HomeInfo.MemberBean;
+import com.rdiot.yx485.databinding.ItemPersonListBinding;
+import com.rdiot.yx485.http.Url;
+
+import java.util.List;
+
+public class HomePersonAdapter extends RecyclerView.Adapter<HomePersonAdapter.DeviceViewHolder> {
+
+    private final List<MemberBean> mMemberBeanList;
+    private final OnMemberClickListener mOnMemberClickListener;
+    private Context mContext;
+    private String mAdminId;
+
+    public HomePersonAdapter(List<MemberBean> mMemberBeanList, OnMemberClickListener listener, String adminId) {
+        this.mMemberBeanList = mMemberBeanList;
+        mOnMemberClickListener = listener;
+        mAdminId = adminId;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        mContext = parent.getContext();
+        ItemPersonListBinding binding = ItemPersonListBinding.inflate(LayoutInflater.from(mContext), parent, false);
+        return new DeviceViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindView(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mMemberBeanList == null ? 1 : mMemberBeanList.size() + 1;
+    }
+
+    public interface OnMemberClickListener {
+
+        void onMemberClick(MemberBean member);
+
+        void onAddMemberClick();
+
+    }
+
+    class DeviceViewHolder extends RecyclerView.ViewHolder {
+        private final ItemPersonListBinding mBinding;
+
+        public DeviceViewHolder(@NonNull ItemPersonListBinding itemView) {
+            super(itemView.getRoot());
+            mBinding = itemView;
+        }
+
+        public void bindView(int position) {
+            if (position >= mMemberBeanList.size()) {
+                mBinding.pername.setText("邀请家人");
+                mBinding.image.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.round_add_40));
+                mBinding.getRoot().setOnClickListener(v -> {
+                    if (mOnMemberClickListener != null) {
+                        mOnMemberClickListener.onAddMemberClick();
+                    }
+                });
+                return;
+            }
+
+            MemberBean memberBean = mMemberBeanList.get(position);
+            if (!TextUtils.isEmpty(memberBean.user_name)) {
+                mBinding.pername.setText(memberBean.user_name);
+            }
+
+            if (!TextUtils.isEmpty(memberBean.photo)) {
+                Glide.with(mContext)
+                        .load(Url.BASE_URL + memberBean.photo)
+                        .error(Glide.with(mContext).load(R.drawable.tx))
+                        .into(mBinding.image);
+            }
+
+            mBinding.tag.setVisibility(memberBean.user_id.equals(mAdminId) ? View.VISIBLE : View.GONE);
+            mBinding.getRoot().setOnClickListener(v -> {
+                if (mOnMemberClickListener != null) {
+                    mOnMemberClickListener.onMemberClick(memberBean);
+                }
+            });
+        }
+    }
+}

+ 86 - 0
app/src/main/java/com/rdiot/yx485/adapter/HotListAdapter.java

@@ -0,0 +1,86 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.GoodListBean.DataBean.GoodBean;
+import com.rdiot.yx485.databinding.ViewDiscoverItemBinding;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.webview.WebViewActivity;
+
+import java.util.List;
+
+public class HotListAdapter extends RecyclerView.Adapter<HotListAdapter.ViewHolder> {
+
+    private final List<GoodBean> mList;
+    private final Context mContext;
+
+    public HotListAdapter(Context context, List<GoodBean> list) {
+        mContext = context;
+        mList = list;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        ViewDiscoverItemBinding binding = ViewDiscoverItemBinding.inflate(LayoutInflater.from(mContext), parent, false);
+        return new ViewHolder(binding);
+    }
+
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindData(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mList == null ? 0 : mList.size();
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        private final ViewDiscoverItemBinding mBinding;
+
+        ViewHolder(ViewDiscoverItemBinding itemView) {
+            super(itemView.getRoot());
+            mBinding = itemView;
+        }
+
+        public void bindData(int position) {
+            if (position >= getItemCount()) {
+                return;
+            }
+
+            GoodBean item = mList.get(position);
+            if (item == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(item.name)) {
+                mBinding.content.setText(item.name);
+            }
+
+            mBinding.price.setText(String.format("¥%s", item.price));
+            if (!TextUtils.isEmpty(item.image)) {
+                Glide.with(mBinding.goodpic.getContext())
+                        .load(Url.BASE_URL + item.image)
+                        .error(Glide.with(mBinding.goodpic.getContext()).load(R.drawable.find_d))
+                        .into(mBinding.goodpic);
+            }
+
+            if (!TextUtils.isEmpty(item.detail_url)) {
+                mBinding.getRoot().setOnClickListener(v -> {
+                    WebViewActivity.newIntent(mContext, item.detail_url, null);
+                });
+            }
+        }
+    }
+}

+ 33 - 0
app/src/main/java/com/rdiot/yx485/adapter/MainHomePagerAdapter.java

@@ -0,0 +1,33 @@
+package com.rdiot.yx485.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainHomePagerAdapter extends FragmentStateAdapter {
+    private final List<Fragment> mFragments = new ArrayList<>();
+
+    public MainHomePagerAdapter(FragmentActivity fm, List<Fragment> fragments) {
+        super(fm);
+        mFragments.clear();
+        mFragments.addAll(fragments);
+    }
+
+    @NonNull
+    @Override
+    public Fragment createFragment(int position) {
+        if (position >= mFragments.size()) {
+            return new Fragment();
+        }
+        return mFragments.get(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mFragments.size();
+    }
+}

+ 97 - 0
app/src/main/java/com/rdiot/yx485/adapter/MessageListAdapter.java

@@ -0,0 +1,97 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.MessageListBean;
+import com.rdiot.yx485.webview.WebViewActivity;
+
+import java.util.List;
+
+public class MessageListAdapter extends RecyclerView.Adapter<MessageListAdapter.ViewHolder> {
+
+    public List<MessageListBean.DataBean.MessageInfo> mData;
+    private Context mContext;
+    private OnMessageReadListener mReadListener;
+
+    public MessageListAdapter(Context context, List<MessageListBean.DataBean.MessageInfo> data) {
+        mContext = context;
+        this.mData = data;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(mContext).inflate(R.layout.message_item, parent, false);
+        return new ViewHolder(view);
+    }
+
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        if (!TextUtils.isEmpty(mData.get(position).title)) {
+            holder.name.setText(mData.get(position).title);
+        }
+        if (!TextUtils.isEmpty(mData.get(position).content)) {
+            holder.message_content.setText(mData.get(position).content);
+        }
+        if (!TextUtils.isEmpty(mData.get(position).created_at)) {
+            holder.message_date.setText(mData.get(position).created_at);
+        }
+        if ("1".equals(mData.get(position).status)) {
+            holder.message_unread.setVisibility(View.VISIBLE);
+        } else {
+            holder.message_unread.setVisibility(View.GONE);
+        }
+        if (!TextUtils.isEmpty(mData.get(position).detail_url)) {
+            holder.itemView.setOnClickListener(v -> {
+                        if (mReadListener != null) {
+                            mReadListener.onMessageRead(mData.get(position).record_id);
+                        }
+                        WebViewActivity.newIntent(mContext, mData.get(position).detail_url, null);
+                    }
+
+            );
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        if (mData == null) {
+            return 0;
+        }
+        return mData.size();
+    }
+
+    public void setReadListener(OnMessageReadListener readListener) {
+        mReadListener = readListener;
+    }
+
+    public interface OnMessageReadListener {
+        void onMessageRead(String messageId);
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+
+        TextView name;
+        TextView message_unread;
+        TextView message_date;
+        TextView message_content;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            message_date = itemView.findViewById(R.id.message_date);
+            name = itemView.findViewById(R.id.message_title);
+            message_unread = itemView.findViewById(R.id.message_unread);
+            message_content = itemView.findViewById(R.id.message_content);
+        }
+    }
+}

+ 71 - 0
app/src/main/java/com/rdiot/yx485/adapter/RoomAdapter.java

@@ -0,0 +1,71 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Intent;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.DeviceRoomListBean;
+import com.rdiot.yx485.room.RoomSetDeviceActivity;
+
+import java.util.List;
+
+public class RoomAdapter extends RecyclerView.Adapter<RoomAdapter.RoomViewHolder> {
+
+    private List<DeviceRoomListBean.RoomBean> roomList;
+
+    public RoomAdapter(List<DeviceRoomListBean.RoomBean> roomList) {
+        this.roomList = roomList;
+    }
+
+    @NonNull
+    @Override
+    public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.room_item, parent, false);
+        return new RoomViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RoomViewHolder holder, int position) {
+        DeviceRoomListBean.RoomBean room = roomList.get(position);
+        if (!TextUtils.isEmpty(room.name)) {
+            holder.roomName.setText(room.name);
+        }
+        int deviceCount = room.devices == null ? 0 : room.devices.size();
+        holder.deviceCount.setText(deviceCount + "个设备");
+        holder.deviceCount.setOnClickListener(v -> {
+            Intent intent = new Intent(holder.deviceCount.getContext(), RoomSetDeviceActivity.class);
+            intent.putExtra("roomid", room.record_id);
+            intent.putExtra("number", deviceCount);
+            intent.putExtra("name", room.name);
+            holder.deviceCount.getContext().startActivity(intent);
+        });
+
+        holder.bottomLine.setVisibility(position == roomList.size() - 1 ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    @Override
+    public int getItemCount() {
+        return roomList.size();
+    }
+
+    static class RoomViewHolder extends RecyclerView.ViewHolder {
+        TextView roomName;
+        TextView deviceCount;
+
+        View bottomLine;
+
+        RoomViewHolder(View itemView) {
+            super(itemView);
+            roomName = itemView.findViewById(R.id.room_name);
+            deviceCount = itemView.findViewById(R.id.device_count);
+            bottomLine = itemView.findViewById(R.id.bottom_line);
+        }
+    }
+}

+ 80 - 0
app/src/main/java/com/rdiot/yx485/adapter/RoomDeteleDeviceAdapter.java

@@ -0,0 +1,80 @@
+package com.rdiot.yx485.adapter;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.RoomDeviceDetailBean;
+import com.rdiot.yx485.http.Url;
+
+import java.util.List;
+
+public class RoomDeteleDeviceAdapter extends RecyclerView.Adapter<RoomDeteleDeviceAdapter.DeviceViewHolder> {
+
+    private List<RoomDeviceDetailBean.RoomDeviceList.RoomDevice> devices;
+    private OnItemClickListener listener;
+
+    public RoomDeteleDeviceAdapter(List<RoomDeviceDetailBean.RoomDeviceList.RoomDevice> devices, OnItemClickListener listener) {
+        this.devices = devices;
+        this.listener = listener;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.room_device_item_detele, parent, false);
+        return new DeviceViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        RoomDeviceDetailBean.RoomDeviceList.RoomDevice device = devices.get(position);
+        if (!TextUtils.isEmpty(device.name)) {
+            holder.deviceName.setText(device.name);
+        }
+        if (device.icon != null) {
+            Glide.with(holder.deviceIcon.getContext())
+                    .load(Url.BASE_URL + device.icon)
+                    .error(Glide.with(holder.deviceIcon.getContext()).load(R.drawable.find_d))
+                    .into(holder.deviceIcon);
+        }
+        holder.devicepuls.setOnClickListener(view -> {
+            if (listener != null) {
+                listener.onItemClick(device);
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        if (devices == null) {
+            return 0;
+        }
+        return devices.size();
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(RoomDeviceDetailBean.RoomDeviceList.RoomDevice device);
+    }
+
+    static class DeviceViewHolder extends RecyclerView.ViewHolder {
+        ImageView deviceIcon;
+        ImageView devicepuls;
+        TextView deviceName;
+
+        public DeviceViewHolder(@NonNull View itemView) {
+            super(itemView);
+            deviceIcon = itemView.findViewById(R.id.deviceIcon);
+            deviceName = itemView.findViewById(R.id.deviceName);
+            devicepuls = itemView.findViewById(R.id.devicepuls);
+        }
+    }
+}

+ 80 - 0
app/src/main/java/com/rdiot/yx485/adapter/RoomDeviceAdapter.java

@@ -0,0 +1,80 @@
+package com.rdiot.yx485.adapter;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.RoomDeviceDetailBean;
+import com.rdiot.yx485.http.Url;
+
+import java.util.List;
+
+public class RoomDeviceAdapter extends RecyclerView.Adapter<RoomDeviceAdapter.DeviceViewHolder> {
+
+    private List<RoomDeviceDetailBean.RoomDeviceList.RoomDevice> devices;
+    private OnItemClickListener listener;
+
+    public RoomDeviceAdapter(List<RoomDeviceDetailBean.RoomDeviceList.RoomDevice> devices, OnItemClickListener listener) {
+        this.devices = devices;
+        this.listener = listener;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.room_device_item, parent, false);
+        return new DeviceViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        RoomDeviceDetailBean.RoomDeviceList.RoomDevice device = devices.get(position);
+        if (!TextUtils.isEmpty(device.name)) {
+            holder.deviceName.setText(device.name);
+        }
+        if (device.icon != null) {
+            Glide.with(holder.deviceIcon.getContext())
+                    .load(Url.BASE_URL + device.icon)
+                    .error(Glide.with(holder.deviceIcon.getContext()).load(R.drawable.find_d))
+                    .into(holder.deviceIcon);
+        }
+        holder.deviceadd.setOnClickListener(view -> {
+            if (listener != null) {
+                listener.onItemClick(device);
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        if (devices == null) {
+            return 0;
+        }
+        return devices.size();
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(RoomDeviceDetailBean.RoomDeviceList.RoomDevice device);
+    }
+
+    static class DeviceViewHolder extends RecyclerView.ViewHolder {
+        ImageView deviceIcon;
+        ImageView deviceadd;
+        TextView deviceName;
+
+        public DeviceViewHolder(@NonNull View itemView) {
+            super(itemView);
+            deviceIcon = itemView.findViewById(R.id.deviceIcon);
+            deviceName = itemView.findViewById(R.id.deviceName);
+            deviceadd = itemView.findViewById(R.id.deviceadd);
+        }
+    }
+}

+ 80 - 0
app/src/main/java/com/rdiot/yx485/adapter/RoomEditAdapter.java

@@ -0,0 +1,80 @@
+package com.rdiot.yx485.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.rdiot.yx485.bean.DeviceRoomListBean;
+import com.rdiot.yx485.databinding.RoomItemEditBinding;
+
+import java.util.List;
+
+public class RoomEditAdapter extends RecyclerView.Adapter<RoomEditAdapter.ViewHolder> {
+    private final OnDeleteClickListener mDeleteClickListener;
+    private final List<DeviceRoomListBean.RoomBean> mData;
+
+    public RoomEditAdapter(List<DeviceRoomListBean.RoomBean> data, OnDeleteClickListener listener) {
+        mData = data;
+        mDeleteClickListener = listener;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        RoomItemEditBinding binding = RoomItemEditBinding.inflate(
+                LayoutInflater.from(parent.getContext()),
+                parent,
+                false
+        );
+        return new ViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        DeviceRoomListBean.RoomBean room = mData.get(position);
+        holder.binding.roomName.setText(room.name);
+
+        holder.binding.roomIcon.setOnClickListener(v -> {
+            if (mDeleteClickListener != null) {
+                mDeleteClickListener.onDeleteClick(room.record_id);
+            }
+        });
+
+        int count = room.devices == null ? 0 : room.devices.size();
+        String deviceCount = String.format("%s个设备", count);
+        holder.binding.deviceCount.setText(deviceCount);
+//        holder.binding.deviceCount.setOnClickListener(view -> {
+//            Intent intent = new Intent(holder.binding.deviceCount.getContext(), RoomSetDeviceActivity.class);
+//            intent.putExtra("roomid", room.record_id);
+//            intent.putExtra("number", deviceCount);
+//            intent.putExtra("name", room.name);
+//            holder.binding.deviceCount.getContext().startActivity(intent);
+//        });
+        holder.binding.bottomLine.setVisibility(position == mData.size() - 1 ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mData.size();
+    }
+
+    public List<DeviceRoomListBean.RoomBean> getData() {
+        return mData;
+    }
+
+    public interface OnDeleteClickListener {
+        void onDeleteClick(String roomId);
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        RoomItemEditBinding binding;
+
+        ViewHolder(RoomItemEditBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+        }
+    }
+}

+ 49 - 0
app/src/main/java/com/rdiot/yx485/adapter/RoomSpinnerAdapter.java

@@ -0,0 +1,49 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.rdiot.yx485.bean.DeviceRoomListBean;
+
+import java.util.List;
+
+public class RoomSpinnerAdapter extends BaseAdapter {
+    private final Context context;
+    private final List<DeviceRoomListBean.RoomBean> items;
+
+    public RoomSpinnerAdapter(Context context, List<DeviceRoomListBean.RoomBean> items) {
+        this.context = context;
+        this.items = items;
+    }
+
+    @Override
+    public int getCount() {
+        return items.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return items.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        if (convertView == null) {
+            convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_spinner_dropdown_item, parent, false);
+        }
+
+        TextView text = convertView.findViewById(android.R.id.text1);
+        text.setText(items.get(position).name);
+
+        return convertView;
+    }
+}

+ 105 - 0
app/src/main/java/com/rdiot/yx485/adapter/TopDeviceAdapter.java

@@ -0,0 +1,105 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.HomeDefBean;
+import com.rdiot.yx485.databinding.ItemListTopBinding;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.webview.WebViewActivity;
+
+import java.util.List;
+
+public class TopDeviceAdapter extends RecyclerView.Adapter<TopDeviceAdapter.DeviceViewHolder> {
+    private OnItemClickListener listener;
+    private List<HomeDefBean.DataBean.RoomBean.DevicesBean> devices;
+    private Context mContext;
+
+    public TopDeviceAdapter(List<HomeDefBean.DataBean.RoomBean.DevicesBean> devices, OnItemClickListener listener) {
+        this.devices = devices;
+        this.listener = listener;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        mContext = parent.getContext();
+        ItemListTopBinding binding = ItemListTopBinding.inflate(LayoutInflater.from(mContext), parent, false);
+        return new DeviceViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bind(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        if (devices == null) {
+            return 0;
+        }
+        return devices.size();
+    }
+
+    public interface OnItemClickListener {
+        void onChecked(int position, Boolean isChecked);
+    }
+
+    public class DeviceViewHolder extends RecyclerView.ViewHolder {
+        ItemListTopBinding mBinding;
+
+        public DeviceViewHolder(@NonNull ItemListTopBinding itemView) {
+            super(itemView.getRoot());
+            mBinding = itemView;
+        }
+
+        public void bind(int position) {
+            HomeDefBean.DataBean.RoomBean.DevicesBean device = devices.get(position);
+            if (device == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(device.icon)) {
+                Glide.with(mContext)
+                        .load(Url.BASE_URL + device.icon)
+                        .error(Glide.with(mContext).load(R.drawable.find_d))
+                        .into(mBinding.image);
+                // holder.deviceIcon.setImageResource(device.icon);
+            }
+
+            mBinding.name.setText(device.name);
+            if (device.show_online) {
+                mBinding.stae.setText(device.is_online ? "设备在线" : "设备离线");
+            } else {
+                mBinding.stae.setText(String.format("%s℃ |%s%%", device.temperature, device.humidity));
+            }
+
+            if (device.show_power) {
+                mBinding.toggle.setVisibility(View.VISIBLE);
+                mBinding.toggle.setChecked(device.power == 1);
+                mBinding.toggle.setOnCheckedChangeListener((compoundButton, b) -> {
+                    if (listener != null) {
+                        listener.onChecked(position, b);
+                    }
+                });
+            } else {
+                mBinding.toggle.setVisibility(View.GONE);
+            }
+
+            mBinding.getRoot().setOnClickListener(v -> {
+                if (!TextUtils.isEmpty(device.detail_url)) {
+                    WebViewActivity.newIntent(mContext, device.detail_url, null);
+                }
+            });
+        }
+    }
+}

+ 101 - 0
app/src/main/java/com/rdiot/yx485/adapter/TopDevicegridAdapter.java

@@ -0,0 +1,101 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.bean.HomeDefBean;
+import com.rdiot.yx485.databinding.ItemGridelistTopBinding;
+import com.rdiot.yx485.http.Url;
+import com.rdiot.yx485.webview.WebViewActivity;
+
+import java.util.List;
+
+public class TopDevicegridAdapter extends RecyclerView.Adapter<TopDevicegridAdapter.DeviceViewHolder> {
+
+    private final List<HomeDefBean.DataBean.RoomBean.DevicesBean> devices;
+    OnItemCheckedListener onItemCheckedListener;
+    private Context mContext;
+
+    public TopDevicegridAdapter(List<HomeDefBean.DataBean.RoomBean.DevicesBean> devices, OnItemCheckedListener listener) {
+        this.devices = devices;
+        this.onItemCheckedListener = listener;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        mContext = parent.getContext();
+        ItemGridelistTopBinding binding = ItemGridelistTopBinding.inflate(LayoutInflater.from(mContext), parent, false);
+        return new DeviceViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bindView(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        if (devices == null) {
+            return 0;
+        }
+        return devices.size();
+    }
+
+    public interface OnItemCheckedListener {
+        void onChecked(int position, Boolean isChecked);
+    }
+
+    public class DeviceViewHolder extends RecyclerView.ViewHolder {
+        ItemGridelistTopBinding mBinding;
+
+        public DeviceViewHolder(@NonNull ItemGridelistTopBinding itemView) {
+            super(itemView.getRoot());
+            mBinding = itemView;
+        }
+
+        public void bindView(int position) {
+            HomeDefBean.DataBean.RoomBean.DevicesBean device = devices.get(position);
+            mBinding.name.setText(device.name);
+            mBinding.stae.setText(device.temperature + "℃|" + device.humidity + "%");
+            if (device.icon != null) {
+                Glide.with(mContext)
+                        .load(Url.BASE_URL + device.icon)
+                        .into(mBinding.image);
+                // holder.deviceIcon.setImageResource(device.icon);
+            }
+
+            if (device.show_online) {
+                mBinding.stae.setText(device.is_online ? "设备在线" : "设备离线");
+            } else {
+                mBinding.stae.setText(String.format("%s℃ |%s%%", device.temperature, device.humidity));
+            }
+
+            if (device.show_power) {
+                mBinding.toggle.setVisibility(View.VISIBLE);
+                mBinding.toggle.setChecked(device.power == 1);
+                mBinding.toggle.setOnCheckedChangeListener((compoundButton, b) -> {
+                    if (onItemCheckedListener != null) {
+                        onItemCheckedListener.onChecked(position, b);
+                    }
+                });
+            } else {
+                mBinding.toggle.setVisibility(View.GONE);
+            }
+
+            mBinding.getRoot().setOnClickListener(v -> {
+                if (!TextUtils.isEmpty(device.detail_url)) {
+                    WebViewActivity.newIntent(mContext, device.detail_url, null);
+                }
+            });
+        }
+    }
+}

+ 94 - 0
app/src/main/java/com/rdiot/yx485/adapter/TopModelAdapter.java

@@ -0,0 +1,94 @@
+package com.rdiot.yx485.adapter;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.bean.ModelRepleBean;
+import com.rdiot.yx485.databinding.ItemListModelBinding;
+import com.rdiot.yx485.http.Url;
+import com.zcolin.frame.util.DisplayUtil;
+
+import java.util.List;
+
+public class TopModelAdapter extends RecyclerView.Adapter<TopModelAdapter.DeviceViewHolder> {
+
+    private List<ModelRepleBean.DataBean> devices;
+    private OnItemClickListener listener;
+    private Context mContext;
+
+    public TopModelAdapter(List<ModelRepleBean.DataBean> devices, OnItemClickListener listener) {
+        this.devices = devices;
+        this.listener = listener;
+    }
+
+    @NonNull
+    @Override
+    public DeviceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        mContext = parent.getContext();
+        ItemListModelBinding binding = ItemListModelBinding.inflate(
+                LayoutInflater.from(mContext), parent, false);
+        return new DeviceViewHolder(binding);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull DeviceViewHolder holder, int position) {
+        holder.setIsRecyclable(false);
+        holder.bind(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return devices == null ? 0 : devices.size();
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(String id);
+    }
+
+    public class DeviceViewHolder extends RecyclerView.ViewHolder {
+        ItemListModelBinding mBinding;
+
+        public DeviceViewHolder(ItemListModelBinding binding) {
+            super(binding.getRoot());
+            mBinding = binding;
+        }
+
+        public void bind(int position) {
+            // 为第一个item添加左边距
+            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mBinding.getRoot().getLayoutParams();
+            if (position == 0) {
+                params.leftMargin = DisplayUtil.dip2px(mContext, 15);
+            } else {
+                params.leftMargin = 0;
+            }
+            mBinding.getRoot().setLayoutParams(params);
+
+            ModelRepleBean.DataBean device = devices.get(position);
+            if (device == null) {
+                return;
+            }
+
+            if (!TextUtils.isEmpty(device.icon)) {
+                Glide.with(mContext)
+                        .load(Url.BASE_URL + device.icon)
+                        .error(Glide.with(mContext).load(R.drawable.find_d))
+                        .into(mBinding.image);
+            }
+            mBinding.name.setText(device.name);
+            mBinding.tvState.setSelected(device.status);
+
+            mBinding.getRoot().setOnClickListener(v -> {
+                if (listener != null) {
+                    listener.onItemClick(device.record_id);
+                }
+            });
+        }
+    }
+}

+ 56 - 0
app/src/main/java/com/rdiot/yx485/app/App.java

@@ -0,0 +1,56 @@
+/*
+ * *********************************************************
+ *   author   colin
+ *   email    wanglin2046@126.com
+ *   date     18-1-9 上午9:59
+ * ********************************************************
+ */
+
+package com.rdiot.yx485.app;
+
+import android.webkit.WebView;
+
+import com.clj.fastble.BleManager;
+import com.igexin.sdk.PushManager;
+import com.lindroy.iosdialog.IDialog;
+import com.tencent.mmkv.MMKV;
+import com.zcolin.frame.app.BaseApp;
+import com.zcolin.frame.http.okhttp.OkHttpUtils;
+import com.zcolin.frame.http.okhttp.cookie.CookieJarImpl;
+import com.zcolin.frame.http.okhttp.cookie.store.MemoryCookieStore;
+import com.zcolin.frame.http.okhttp.https.HttpsUtils;
+
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * 程序入口
+ */
+public class App extends BaseApp {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        APP_CONTEXT = BaseApp.APP_CONTEXT;
+        initHttpOptions();
+        IDialog.INSTANCE.init(this);
+        MMKV.initialize(this);
+        // 初始化蓝牙库
+        BleManager.getInstance().init(this);
+        PushManager.getInstance().preInit(this);
+    }
+
+    private void initHttpOptions() {
+        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
+        CookieJarImpl cookieJar1 = new CookieJarImpl(new MemoryCookieStore());
+        OkHttpClient.Builder builder = new OkHttpClient.Builder();
+        OkHttpClient okHttpClient = builder.connectTimeout(10000L, TimeUnit.MILLISECONDS)
+                .readTimeout(10000L, TimeUnit.MILLISECONDS)
+                .cookieJar(cookieJar1)
+                .hostnameVerifier((hostname, session) -> true)
+                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
+                .build();
+        OkHttpUtils.initClient(okHttpClient);
+    }
+}

+ 30 - 0
app/src/main/java/com/rdiot/yx485/base/BaseFragment.kt

@@ -0,0 +1,30 @@
+package com.rdiot.yx485.base
+
+import android.content.Context
+import android.os.Bundle
+import android.view.inputmethod.InputMethodManager
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+
+/**
+ * @author Alpha
+ * @date 2023-09-12
+ */
+open class BaseFragment : Fragment() {
+    protected lateinit var mActivity: FragmentActivity
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mActivity = requireActivity()
+    }
+
+    open fun showMessage(message: String) {
+        Toast.makeText(mActivity, message, Toast.LENGTH_SHORT).show()
+    }
+
+    fun hideSoftKeyBroad() {
+        val imm = mActivity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+        imm.hideSoftInputFromWindow(mActivity.window.decorView.windowToken, 0)
+    }
+}

+ 140 - 0
app/src/main/java/com/rdiot/yx485/base/BaseFragmentActivity.java

@@ -0,0 +1,140 @@
+package com.rdiot.yx485.base;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.rdiot.yx485.BaseActivity;
+import com.rdiot.yx485.R;
+import com.rdiot.yx485.databinding.ActivityBaseBinding;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author fanbaozhu
+ * @date 2020/12/10
+ */
+public abstract class BaseFragmentActivity extends BaseActivity implements View.OnClickListener {
+    protected ActivityBaseBinding mBinding;
+    protected FragmentManager mFragmentManager;
+    protected Fragment mCurrentFragment;
+    private List<Fragment> mHistoryList;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinding = ActivityBaseBinding.inflate(getLayoutInflater());
+        setContentView(mBinding.getRoot());
+
+        mBinding.llTitleBar.btnTitleLeft.setOnClickListener(this);
+        mBinding.llTitleBar.btnTitleRight.setOnClickListener(this);
+        mFragmentManager = getSupportFragmentManager();
+        mHistoryList = new ArrayList<>();
+
+        initTitleBar();
+        initView();
+        initData();
+    }
+
+    /**
+     * 初始化标题
+     */
+    public abstract void initTitleBar();
+
+    /**
+     * 初始化界面
+     */
+    public abstract void initView();
+
+    /**
+     * 初始化数据
+     */
+    public void initData() {
+    }
+
+    public void onRightTextClick() {
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mBinding.llTitleBar.btnTitleLeft) {
+            onBackPressed();
+            return;
+        }
+
+        if (v == mBinding.llTitleBar.btnTitleRight) {
+            onRightTextClick();
+        }
+    }
+
+    public void addFragment(Fragment fragment) {
+        if (fragment == null || mHistoryList.contains(fragment)) {
+            return;
+        }
+
+        mHistoryList.add(fragment);
+        mFragmentManager.beginTransaction().add(R.id.fl_container, fragment).commitNow();
+    }
+
+    public void changeFragment(Fragment fragment) {
+        if (fragment == null || mHistoryList.contains(fragment)) {
+            return;
+        }
+
+        if (mCurrentFragment == null) {
+            mFragmentManager.beginTransaction().add(R.id.fl_container, fragment).commitNow();
+            mCurrentFragment = fragment;
+            return;
+        }
+
+        mFragmentManager.beginTransaction().add(R.id.fl_container, fragment).hide(mCurrentFragment).commitNow();
+        mHistoryList.add(mCurrentFragment);
+        mCurrentFragment = fragment;
+    }
+
+    public void goBack(String title) {
+        if (!TextUtils.isEmpty(title)) {
+            mBinding.llTitleBar.tvTitle.setText(title);
+        }
+
+        int count = mHistoryList.size();
+        if (count > 0) {
+            Fragment fragment = mHistoryList.get(count - 1);
+            mHistoryList.remove(fragment);
+            mFragmentManager.beginTransaction().remove(mCurrentFragment).show(fragment).commitNow();
+            mCurrentFragment = fragment;
+            mCurrentFragment.onResume();
+            return;
+        }
+
+        finish();
+    }
+
+    public void removeFragment(Fragment fragment) {
+        if (fragment == null || !mHistoryList.contains(fragment)) {
+            return;
+        }
+
+        mFragmentManager.beginTransaction().remove(fragment).commitNowAllowingStateLoss();
+        mHistoryList.remove(fragment);
+    }
+
+    @Override
+    public void onBackPressed() {
+        int count = mHistoryList.size();
+        if (count > 0) {
+            Fragment fragment = mHistoryList.get(count - 1);
+            mHistoryList.remove(fragment);
+            mFragmentManager.beginTransaction().remove(mCurrentFragment).show(fragment).commitNow();
+            mCurrentFragment = fragment;
+            mCurrentFragment.onResume();
+            return;
+        }
+        super.onBackPressed();
+    }
+}

+ 18 - 0
app/src/main/java/com/rdiot/yx485/base/BaseViewModel.kt

@@ -0,0 +1,18 @@
+package com.rdiot.yx485.base
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.rdiot.yx485.util.LocalCache
+
+open class BaseViewModel : ViewModel() {
+    var mErrorMessage = MutableLiveData<String>()
+    val errorMessage: LiveData<String>
+        get() = mErrorMessage
+
+    protected fun getHeaders(): LinkedHashMap<String?, String?> {
+        val headers = java.util.LinkedHashMap<String?, String?>()
+        headers.put("Authorization", LocalCache.getToken())
+        return headers
+    }
+}

+ 21 - 0
app/src/main/java/com/rdiot/yx485/bean/BannerListBean.java

@@ -0,0 +1,21 @@
+package com.rdiot.yx485.bean;
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.util.List;
+
+public class BannerListBean extends BaseReplyBean {
+
+    public DataBean data;
+
+    public static class DataBean {
+        public List<BannerBean> list;
+
+        public static class BannerBean {
+
+            public String record_id;
+            public String url;
+            public String detail_url;
+        }
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 8 - 0
app/src/main/java/com/rdiot/yx485/bean/CityBean.java


+ 32 - 0
app/src/main/java/com/rdiot/yx485/bean/ConsumableBean.kt

@@ -0,0 +1,32 @@
+package com.rdiot.yx485.bean
+
+import com.rdiot.yx485.http.entity.BaseReplyBean
+
+data class ConsumableBean(
+    val data: DataBean
+) : BaseReplyBean() {
+    
+    data class DataBean(
+        val home_id: String,
+        val desc: String,
+        val enough: Boolean,
+        val devices: List<DeviceInfo>
+    )
+
+    data class DeviceInfo(
+        val device_id: String,
+        val device_name: String,
+        val device_type_name: String,
+        val consumables: List<ConsumableInfo>
+    )
+
+    data class ConsumableInfo(
+        val record_id: String,
+        val device_id: String,
+        val consumable_id: String,
+        val consumable_name: String,
+        val last_updated_at: String,
+        val used_time: Int,          // 已使用天数
+        val user_rate: Double        // 使用率,范围 0-1
+    )
+} 

+ 47 - 0
app/src/main/java/com/rdiot/yx485/bean/CreatRoomBean.java

@@ -0,0 +1,47 @@
+package com.rdiot.yx485.bean;
+
+import java.util.List;
+
+public class CreatRoomBean {
+    /**
+     * name : string
+     * home_id : string
+     * devices : [{"record_id":"string","name":"string","device_code":"string","type_id":"string","type_name":"string","icon":"string","is_master":true,"home_id":"string","room_id":"string","is_online":true,"power":0,"temperature":0,"humidity":0}]
+     */
+
+    public String name;
+    public String home_id;
+    public List<DevicesBean> devices;
+
+    public static class DevicesBean {
+        /**
+         * record_id : string
+         * name : string
+         * device_code : string
+         * type_id : string
+         * type_name : string
+         * icon : string
+         * is_master : true
+         * home_id : string
+         * room_id : string
+         * is_online : true
+         * power : 0
+         * temperature : 0
+         * humidity : 0
+         */
+
+        public String record_id;
+        public String name;
+        public String device_code;
+        public String type_id;
+        public String type_name;
+        public String icon;
+        public boolean is_master;
+        public String home_id;
+        public String room_id;
+        public boolean is_online;
+        public int power;
+        public int temperature;
+        public int humidity;
+    }
+}

+ 45 - 0
app/src/main/java/com/rdiot/yx485/bean/CreateHomeBean.java

@@ -0,0 +1,45 @@
+package com.rdiot.yx485.bean;
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.util.List;
+
+public class CreateHomeBean extends BaseReplyBean {
+    /**
+     * name : string
+     * province : string
+     * city : string
+     * district : string
+     * address : string
+     * member : [{"user_id":"string","remark":"string"}]
+     * is_default : true
+     * room : [{"name":"string"}]
+     */
+
+    public String name;
+    public String province;
+    public String city;
+    public String district;
+    public String address;
+    public boolean is_default;
+    public List<MemberBean> member;
+    public List<RoomBean> room;
+
+    public static class MemberBean {
+        /**
+         * user_id : string
+         * remark : string
+         */
+
+        public String user_id;
+        public String remark;
+    }
+
+    public static class RoomBean {
+        /**
+         * name : string
+         */
+
+        public String name;
+    }
+}

+ 30 - 0
app/src/main/java/com/rdiot/yx485/bean/DefHomeRoomBean.java

@@ -0,0 +1,30 @@
+package com.rdiot.yx485.bean;
+
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class DefHomeRoomBean extends BaseReplyBean implements Serializable {
+    /**
+     * data : {"list":[{"record_id":"string","name":"string"}]}
+     */
+
+    public DataBean data;
+
+    public static class DataBean implements Serializable {
+        public List<CityInfo> list;
+
+        public static class CityInfo implements Serializable {
+            /**
+             * record_id : string
+             * name : string
+             */
+
+            public String record_id;
+            public String name;
+            public List<CityInfo> children;
+        }
+    }
+}

+ 13 - 0
app/src/main/java/com/rdiot/yx485/bean/DeteleRoomBean.java

@@ -0,0 +1,13 @@
+package com.rdiot.yx485.bean;
+
+import java.util.List;
+
+public class DeteleRoomBean {
+    /**
+     * record_ids : ["string"]
+     * home_id : string
+     */
+
+    public String home_id;
+    public List<String> record_ids;
+}

+ 74 - 0
app/src/main/java/com/rdiot/yx485/bean/DeviceRoomListBean.java

@@ -0,0 +1,74 @@
+package com.rdiot.yx485.bean;
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.util.List;
+
+public class DeviceRoomListBean extends BaseReplyBean {
+    public List<RoomBean> data;
+
+    public static class RoomBean {
+        /**
+         * record_id : 1pw4ums27dbd53ot7wsrtjp190gvaunb
+         * name : 客厅
+         * home_id : 1pw4ums27dbd53orkrgy3rn1409jzdla
+         * home_name : 测试的家
+         * devices : [{"record_id":"1pw4umsu3m1cvyfxldn3dgut503o71pc","name":"氟系统","device_code":"083A8D845000","sub_code":"","type_id":"2pw4umsiig2cts5zphbiqwv568pya11","type_code":"YXK-Z/86-FG-A","type_name":"主控制器","icon":"/s/yongxu/1pw4ums98q0d7ym7ra0y7i7kh0jeqx6m/1.png","sequence":2,"is_master":true,"room_id":"1pw4ums27dbd53ot7wsrtjp190gvaunb","is_online":false,"power":1,"temperature":24,"humidity":36}]
+         * sequence : 0
+         * is_master : true
+         * device_type_id : 2pw4umsiig2cts5zphbiqwv568pya11
+         * control_number : 083A8D845000
+         * device_status : 0
+         * valve_name :
+         */
+
+        public String record_id;
+        public String name;
+        public String home_id;
+        public String home_name;
+        public int sequence;
+        public boolean is_master;
+        public String device_type_id;
+        public String control_number;
+        public int device_status;
+        public String valve_name;
+        public boolean isselect;
+        public List<DevicesBean> devices;
+
+        public static class DevicesBean {
+            /**
+             * record_id : 1pw4umsu3m1cvyfxldn3dgut503o71pc
+             * name : 氟系统
+             * device_code : 083A8D845000
+             * sub_code :
+             * type_id : 2pw4umsiig2cts5zphbiqwv568pya11
+             * type_code : YXK-Z/86-FG-A
+             * type_name : 主控制器
+             * icon : /s/yongxu/1pw4ums98q0d7ym7ra0y7i7kh0jeqx6m/1.png
+             * sequence : 2
+             * is_master : true
+             * room_id : 1pw4ums27dbd53ot7wsrtjp190gvaunb
+             * is_online : false
+             * power : 1
+             * temperature : 24
+             * humidity : 36
+             */
+
+            public String record_id;
+            public String name;
+            public String device_code;
+            public String sub_code;
+            public String type_id;
+            public String type_code;
+            public String type_name;
+            public String icon;
+            public int sequence;
+            public boolean is_master;
+            public String room_id;
+            public boolean is_online;
+            public int power;
+            public int temperature;
+            public int humidity;
+        }
+    }
+}

+ 18 - 0
app/src/main/java/com/rdiot/yx485/bean/DeviceStatus.kt

@@ -0,0 +1,18 @@
+package com.rdiot.yx485.bean
+
+
+import com.rdiot.yx485.http.entity.BaseReplyBean
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class DeviceStatus(
+    @SerialName("status")
+    var status: Int = 0, // 0
+)
+
+@Serializable
+data class DeviceOnlineState(
+    @SerialName("data")
+    var data: DeviceStatus
+) : BaseReplyBean()

+ 165 - 0
app/src/main/java/com/rdiot/yx485/bean/DeviceTreeBean.java

@@ -0,0 +1,165 @@
+package com.rdiot.yx485.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.gson.annotations.SerializedName;
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.util.List;
+
+public class DeviceTreeBean extends BaseReplyBean {
+
+    public List<DataBean> data;
+
+    public static class DataBean {
+        /**
+         * record_id : 10w4umsiig2cts62vlix9pe79go05s6w
+         * code : YX-F
+         * name : 氟系统
+         * parent_id :
+         * is_master : false
+         * icon :
+         * children : [{"record_id":"2pw4umsiig2cts5zphbiqwv568pya11","code":"YXK-Z/86-FG-A","name":"主控制器","parent_id":"10w4umsiig2cts62vlix9pe79go05s6w","is_master":true,"icon":"/s/yongxu/1pw4ums98q0d7ym7ra0y7i7kh0jeqx6m/1.png","children":null,"control_url":"","steps":[{"record_id":"1pw4ums1e4rd3gf2pekhflx3g0abj83x","device_type_id":"2pw4umsiig2cts5zphbiqwv568pya11","device_type_name":"","sequence":1,"desc":"1、主控开机状态下,长按『菜单键』3s进入设置界面","photo":"/s/yongxu/1pw4ums98q0d7yljtpve7b4k70s7m6o1/img_reset_1.png"},{"record_id":"1pw4ums1e4rd3gf2per42ia3j041b5e1","device_type_id":"2pw4umsiig2cts5zphbiqwv568pya11","device_type_name":"","sequence":2,"desc":"2、再次长按『菜单键』,直到显示配网二维码","photo":"/s/yongxu/1pw4ums98q0d7ylk958bws1k90iyzmci/img_reset_2.png"}]},{"record_id":"3pw4umsiig2cts62vlix9pe79go05s63","code":"YXK-F/86-FG-A","name":"分控制器","parent_id":"10w4umsiig2cts62vlix9pe79go05s6w","is_master":false,"icon":"/s/yongxu/1pw4ums98q0d7ym81r99lt3kj0sep3ra/2.png","children":null,"control_url":"","steps":[{"record_id":"1pw4ums1fm6d3hez3w0n62pq002w3zz6","device_type_id":"3pw4umsiig2cts62vlix9pe79go05s63","device_type_name":"","sequence":1,"desc":"1、分控开机状态下,长按『菜单键』3s直到界面出现配网二维码","photo":"/s/yongxu/1pw4ums98q0d7ylkhbkfpy7kb04ckcgw/img_sub_reset_1.png"},{"record_id":"1pw4ums1h4jd3ko0i58kj3fr002rtcmy","device_type_id":"3pw4umsiig2cts62vlix9pe79go05s63","device_type_name":"","sequence":2,"desc":"2、使用APP扫码绑定","photo":"/s/yongxu/1pw4ums98q0d7ylkoypnupfkd0p4egmj/img_reset_2.png"}]}]
+         * control_url :
+         * steps : null
+         */
+
+        public String record_id;
+        public String name;
+        public String parent_id;
+        public boolean is_master;
+        public String icon;
+        public boolean need_device_code;
+        public boolean bluetooth;
+        public String control_url;
+        public Object steps;
+        public Boolean isselect;
+        public List<ChildrenBean> children;
+
+        public static class ChildrenBean implements Parcelable {
+            public static final Creator<ChildrenBean> CREATOR = new Creator<ChildrenBean>() {
+                @Override
+                public ChildrenBean createFromParcel(Parcel in) {
+                    return new ChildrenBean(in);
+                }
+
+                @Override
+                public ChildrenBean[] newArray(int size) {
+                    return new ChildrenBean[size];
+                }
+            };
+            /**
+             * record_id : 2pw4umsiig2cts5zphbiqwv568pya11
+             * code : YXK-Z/86-FG-A
+             * name : 主控制器
+             * parent_id : 10w4umsiig2cts62vlix9pe79go05s6w
+             * is_master : true
+             * icon : /s/yongxu/1pw4ums98q0d7ym7ra0y7i7kh0jeqx6m/1.png
+             * children : null
+             * control_url :
+             * steps : [{"record_id":"1pw4ums1e4rd3gf2pekhflx3g0abj83x","device_type_id":"2pw4umsiig2cts5zphbiqwv568pya11","device_type_name":"","sequence":1,"desc":"1、主控开机状态下,长按『菜单键』3s进入设置界面","photo":"/s/yongxu/1pw4ums98q0d7yljtpve7b4k70s7m6o1/img_reset_1.png"},{"record_id":"1pw4ums1e4rd3gf2per42ia3j041b5e1","device_type_id":"2pw4umsiig2cts5zphbiqwv568pya11","device_type_name":"","sequence":2,"desc":"2、再次长按『菜单键』,直到显示配网二维码","photo":"/s/yongxu/1pw4ums98q0d7ylk958bws1k90iyzmci/img_reset_2.png"}]
+             */
+
+            public String record_id;
+            @SerializedName("code")
+            public String codeX;
+            public String name;
+            public String parent_id;
+            public boolean is_master;
+            //是否需要输入设备号
+            public boolean need_device_code;
+            //是否需要通过蓝牙连接
+            public boolean bluetooth;
+            public String icon;
+            public Object children;
+            public String control_url;
+            public String blue_code;
+            public List<StepsBean> steps;
+
+            protected ChildrenBean(Parcel in) {
+                record_id = in.readString();
+                codeX = in.readString();
+                name = in.readString();
+                parent_id = in.readString();
+                is_master = in.readByte() != 0;
+                need_device_code = in.readByte() != 0;
+                bluetooth = in.readByte() != 0;
+                icon = in.readString();
+                control_url = in.readString();
+                blue_code = in.readString();
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {
+                dest.writeString(record_id);
+                dest.writeString(codeX);
+                dest.writeString(name);
+                dest.writeString(parent_id);
+                dest.writeByte((byte) (is_master ? 1 : 0));
+                dest.writeByte((byte) (need_device_code ? 1 : 0));
+                dest.writeByte((byte) (bluetooth ? 1 : 0));
+                dest.writeString(icon);
+                dest.writeString(control_url);
+            }
+
+            public static class StepsBean implements Parcelable {
+                public static final Creator<StepsBean> CREATOR = new Creator<StepsBean>() {
+                    @Override
+                    public StepsBean createFromParcel(Parcel in) {
+                        return new StepsBean(in);
+                    }
+
+                    @Override
+                    public StepsBean[] newArray(int size) {
+                        return new StepsBean[size];
+                    }
+                };
+                /**
+                 * record_id : 1pw4ums1e4rd3gf2pekhflx3g0abj83x
+                 * device_type_id : 2pw4umsiig2cts5zphbiqwv568pya11
+                 * device_type_name :
+                 * sequence : 1
+                 * desc : 1、主控开机状态下,长按『菜单键』3s进入设置界面
+                 * photo : /s/yongxu/1pw4ums98q0d7yljtpve7b4k70s7m6o1/img_reset_1.png
+                 */
+
+                public String record_id;
+                public String device_type_id;
+                public String device_type_name;
+                public int sequence;
+                public String desc;
+                public String photo;
+
+                protected StepsBean(Parcel in) {
+                    record_id = in.readString();
+                    device_type_id = in.readString();
+                    device_type_name = in.readString();
+                    sequence = in.readInt();
+                    desc = in.readString();
+                    photo = in.readString();
+                }
+
+                @Override
+                public int describeContents() {
+                    return 0;
+                }
+
+                @Override
+                public void writeToParcel(Parcel dest, int flags) {
+                    dest.writeString(record_id);
+                    dest.writeString(device_type_id);
+                    dest.writeString(device_type_name);
+                    dest.writeInt(sequence);
+                    dest.writeString(desc);
+                    dest.writeString(photo);
+                }
+            }
+        }
+    }
+}

+ 7 - 0
app/src/main/java/com/rdiot/yx485/bean/DeviceidBean.java

@@ -0,0 +1,7 @@
+package com.rdiot.yx485.bean;
+
+import java.util.List;
+
+public class DeviceidBean {
+    public List<String> record_ids;
+}

+ 24 - 0
app/src/main/java/com/rdiot/yx485/bean/EditDeviceReply.java

@@ -0,0 +1,24 @@
+package com.rdiot.yx485.bean;
+
+import java.util.List;
+
+public class EditDeviceReply {
+
+    /**
+     * device_id : string
+     * room_id : string
+     */
+
+    public List<DevicesBean> devices;
+    public String name;
+
+    public static class DevicesBean {
+        public DevicesBean(String device_id, String room_id) {
+            this.device_id = device_id;
+            this.room_id = room_id;
+        }
+
+        public String device_id;
+        public String room_id;
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 10 - 0
app/src/main/java/com/rdiot/yx485/bean/EditDeviceReplyBean.java


+ 42 - 0
app/src/main/java/com/rdiot/yx485/bean/GoodListBean.java

@@ -0,0 +1,42 @@
+package com.rdiot.yx485.bean;
+
+
+import com.rdiot.yx485.http.entity.BaseReplyBean;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class GoodListBean extends BaseReplyBean {
+    /**
+     * data : {"list":[{"record_id":"string","name":"string","description":"string","price":0,"category_code":"string","sequence":0,"is_recommend":true,"image":"string"}]}
+     */
+
+    public DataBean data;
+
+    public static class DataBean {
+        public List<GoodBean> list;
+
+        public static class GoodBean implements Serializable {
+            /**
+             * record_id : string
+             * name : string
+             * description : string
+             * price : 0
+             * category_code : string
+             * sequence : 0
+             * is_recommend : true
+             * image : string
+             */
+
+            public String record_id;
+            public String name;
+            public String description;
+            public int price;
+            public String category_code;
+            public int sequence;
+            public boolean is_recommend;
+            public String image;
+            public String detail_url;
+        }
+    }
+}

+ 61 - 0
app/src/main/java/com/rdiot/yx485/bean/HomeBase.java

@@ -0,0 +1,61 @@
+package com.rdiot.yx485.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class HomeBase implements Parcelable {
+    private String id;
+    private String name;
+
+    public HomeBase() {
+    }
+
+    public HomeBase(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    protected HomeBase(Parcel in) {
+        id = in.readString();
+        name = in.readString();
+    }
+
+    public static final Creator<HomeBase> CREATOR = new Creator<HomeBase>() {
+        @Override
+        public HomeBase createFromParcel(Parcel in) {
+            return new HomeBase(in);
+        }
+
+        @Override
+        public HomeBase[] newArray(int size) {
+            return new HomeBase[size];
+        }
+    };
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(id);
+        dest.writeString(name);
+    }
+} 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 9 - 0
app/src/main/java/com/rdiot/yx485/bean/HomeDefBean.java


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov