liuxiulin 1 year ago
parent
commit
c3854a14bc
45 changed files with 715 additions and 243 deletions
  1. 1 2
      FastBleLib/build.gradle
  2. 1 2
      FastBleLib/src/main/AndroidManifest.xml
  3. 3 2
      FastBleLib/src/main/java/com/clj/fastble/BleManager.java
  4. 1 2
      RangeSeekBar/build.gradle
  5. 1 2
      RangeSeekBar/src/main/AndroidManifest.xml
  6. 7 4
      app/build.gradle
  7. 1 2
      app/src/main/AndroidManifest.xml
  8. 1 0
      app/src/main/java/com/rdiot/yx485/adapter/SelectDeviceTypeAdapter.kt
  9. 4 0
      app/src/main/java/com/rdiot/yx485/base/LocalData.kt
  10. 15 0
      app/src/main/java/com/rdiot/yx485/bean/DeviceStatus.kt
  11. 9 2
      app/src/main/java/com/rdiot/yx485/bean/RoomData.kt
  12. 29 1
      app/src/main/java/com/rdiot/yx485/net/Api.kt
  13. 24 1
      app/src/main/java/com/rdiot/yx485/ui/bind/BindActivity.kt
  14. 44 5
      app/src/main/java/com/rdiot/yx485/ui/bind/BindDeviceFragment.kt
  15. 29 1
      app/src/main/java/com/rdiot/yx485/ui/bind/EnterRoomInfoFragment.kt
  16. 6 6
      app/src/main/java/com/rdiot/yx485/ui/bind/SelectDeviceTypeFragment.kt
  17. 3 1
      app/src/main/java/com/rdiot/yx485/ui/bind/ble/BindBean.kt
  18. 26 8
      app/src/main/java/com/rdiot/yx485/ui/bind/ble/BleCtrl.kt
  19. 37 1
      app/src/main/java/com/rdiot/yx485/ui/bind/model/BindViewModel.kt
  20. 25 0
      app/src/main/java/com/rdiot/yx485/ui/ctrl/RoomCtrlMainActivity.kt
  21. 1 1
      app/src/main/java/com/rdiot/yx485/ui/ctrl/SubRoomCtrlFragment.kt
  22. 7 7
      app/src/main/java/com/rdiot/yx485/ui/home/HomeFragment.kt
  23. 1 0
      app/src/main/java/com/rdiot/yx485/ui/login/LoginPwdFragment.kt
  24. 2 0
      app/src/main/java/com/rdiot/yx485/ui/main/MainActivity.kt
  25. 1 1
      app/src/main/java/com/rdiot/yx485/ui/mine/MineFragment.kt
  26. 31 1
      app/src/main/java/com/rdiot/yx485/ui/mine/UserInfoActivity.kt
  27. 5 1
      app/src/main/java/com/rdiot/yx485/ui/room/RoomFragment.kt
  28. 122 48
      app/src/main/java/com/rdiot/yx485/ui/web/WebActivity.kt
  29. 42 0
      app/src/main/java/com/rdiot/yx485/util/RoomUtils.kt
  30. 5 6
      app/src/main/res/layout/act_web.xml
  31. 2 0
      app/src/main/res/layout/fra_enter_room_info.xml
  32. 4 3
      app/src/main/res/layout/fra_home.xml
  33. 1 1
      app/src/main/res/layout/fra_mine.xml
  34. 44 1
      app/src/main/res/layout/fra_reset.xml
  35. 11 11
      app/src/main/res/layout/item_room.xml
  36. BIN
      app/src/main/res/mipmap-xxhdpi/img_infrared_reset.png
  37. 5 1
      app/src/main/res/values/strings.xml
  38. 4 5
      arcseekbar/build.gradle
  39. 1 2
      arcseekbar/src/main/AndroidManifest.xml
  40. 2 2
      build.gradle
  41. BIN
      gradle/wrapper/gradle-wrapper.jar
  42. 1 1
      gradle/wrapper/gradle-wrapper.properties
  43. 153 104
      gradlew
  44. 2 3
      pageview/build.gradle
  45. 1 2
      pageview/src/main/AndroidManifest.xml

+ 1 - 2
FastBleLib/build.gradle

@@ -7,8 +7,6 @@ android {
     defaultConfig {
     defaultConfig {
         minSdkVersion 14
         minSdkVersion 14
         targetSdkVersion 30
         targetSdkVersion 30
-        versionCode 240
-        versionName "2.4.0"
     }
     }
     buildTypes {
     buildTypes {
         release {
         release {
@@ -16,6 +14,7 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
         }
     }
     }
+    namespace 'com.clj.fastble'
 }
 }
 
 
 dependencies {
 dependencies {

+ 1 - 2
FastBleLib/src/main/AndroidManifest.xml

@@ -1,6 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="com.clj.fastble">
+    xmlns:tools="http://schemas.android.com/tools">
 
 
 
 
     <!-- Android 12以下配置的三个蓝牙权限 -->
     <!-- Android 12以下配置的三个蓝牙权限 -->

+ 3 - 2
FastBleLib/src/main/java/com/clj/fastble/BleManager.java

@@ -560,12 +560,13 @@ public class BleManager {
      * @param data
      * @param data
      * @param callback
      * @param callback
      */
      */
-    public void write(BleDevice bleDevice,
+    public void writeData(BleDevice bleDevice,
                       String uuid_service,
                       String uuid_service,
                       String uuid_write,
                       String uuid_write,
                       byte[] data,
                       byte[] data,
+                      boolean split,
                       BleWriteCallback callback) {
                       BleWriteCallback callback) {
-        write(bleDevice, uuid_service, uuid_write, data, true, callback);
+        write(bleDevice, uuid_service, uuid_write, data, split, callback);
     }
     }
 
 
     /**
     /**

+ 1 - 2
RangeSeekBar/build.gradle

@@ -8,8 +8,6 @@ android {
     defaultConfig {
     defaultConfig {
         minSdkVersion 16
         minSdkVersion 16
         targetSdkVersion 28
         targetSdkVersion 28
-        versionCode 7
-        versionName "3.0.0"
 
 
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
 
 
@@ -20,6 +18,7 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
         }
     }
     }
+    namespace 'com.jaygoo.widget'
 }
 }
 
 
 dependencies {
 dependencies {

+ 1 - 2
RangeSeekBar/src/main/AndroidManifest.xml

@@ -1,5 +1,4 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.jaygoo.widget">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
 
     <application android:allowBackup="true"
     <application android:allowBackup="true"
                  android:label="@string/app_name"
                  android:label="@string/app_name"

+ 7 - 4
app/build.gradle

@@ -13,8 +13,8 @@ android {
         applicationId "com.rdiot.yx485"
         applicationId "com.rdiot.yx485"
         minSdk 23
         minSdk 23
         targetSdk 32
         targetSdk 32
-        versionCode 4
-        versionName "1.0.4"
+        versionCode 6
+        versionName "1.1.0"
 
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     }
@@ -28,6 +28,8 @@ android {
         }
         }
     }
     }
 
 
+    namespace 'com.rdiot.yx485'
+
     buildTypes {
     buildTypes {
         debug {
         debug {
             signingConfig signingConfigs.release
             signingConfig signingConfigs.release
@@ -62,7 +64,7 @@ android {
 dependencies {
 dependencies {
 
 
     implementation 'androidx.core:core-ktx:1.7.0'
     implementation 'androidx.core:core-ktx:1.7.0'
-    implementation 'androidx.appcompat:appcompat:1.5.1'
+    implementation 'androidx.appcompat:appcompat:1.7.1'
     implementation 'com.google.android.material:material:1.6.1'
     implementation 'com.google.android.material:material:1.6.1'
     implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
     implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
     testImplementation 'junit:junit:4.13.2'
     testImplementation 'junit:junit:4.13.2'
@@ -108,6 +110,7 @@ dependencies {
     // BRV https://github.com/liangjingkanji/BRV
     // BRV https://github.com/liangjingkanji/BRV
     implementation 'com.github.liangjingkanji:BRV:1.3.88'
     implementation 'com.github.liangjingkanji:BRV:1.3.88'
     implementation 'com.google.android.flexbox:flexbox:3.0.0'// flexbox伸缩(流式)布局
     implementation 'com.google.android.flexbox:flexbox:3.0.0'// flexbox伸缩(流式)布局
+    implementation 'com.github.liangjingkanji:Net:3.6.4'
 
 
     //日志记录器
     //日志记录器
     implementation 'com.localebro:okhttpprofiler:1.0.8'
     implementation 'com.localebro:okhttpprofiler:1.0.8'
@@ -144,7 +147,7 @@ dependencies {
     implementation 'com.github.gzu-liyujiang.AndroidPicker:WheelPicker:4.1.11'
     implementation 'com.github.gzu-liyujiang.AndroidPicker:WheelPicker:4.1.11'
 
 
     implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
     implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
-
+    implementation  'com.github.lzyzsd:jsbridge:1.0.5'
 }
 }
 
 
 // Allow references to generated code
 // Allow references to generated code

+ 1 - 2
app/src/main/AndroidManifest.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="com.rdiot.yx485">
+    xmlns:tools="http://schemas.android.com/tools">
 
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

+ 1 - 0
app/src/main/java/com/rdiot/yx485/adapter/SelectDeviceTypeAdapter.kt

@@ -11,6 +11,7 @@ import com.drake.brv.utils.divider
 import com.drake.brv.utils.grid
 import com.drake.brv.utils.grid
 import com.drake.brv.utils.models
 import com.drake.brv.utils.models
 import com.drake.brv.utils.setup
 import com.drake.brv.utils.setup
+import com.king.zxing.util.LogUtils
 import com.rdiot.yx485.R
 import com.rdiot.yx485.R
 import com.rdiot.yx485.bean.DeviceTypeData
 import com.rdiot.yx485.bean.DeviceTypeData
 import com.rdiot.yx485.databinding.ItemDeviceTypeBinding
 import com.rdiot.yx485.databinding.ItemDeviceTypeBinding

+ 4 - 0
app/src/main/java/com/rdiot/yx485/base/LocalData.kt

@@ -72,6 +72,10 @@ object LocalData {
     /** 设备类型 */
     /** 设备类型 */
     val deviceTypeData by serialLiveData(mutableListOf<DeviceTypeData>())
     val deviceTypeData by serialLiveData(mutableListOf<DeviceTypeData>())
 
 
+    var firstBle: Int = 1
+
+    var firstCamera: Boolean by serialLazy(true)
+
     /** 登出 */
     /** 登出 */
     fun logout() {
     fun logout() {
         lastGetCodeTime = 0
         lastGetCodeTime = 0

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

@@ -0,0 +1,15 @@
+package com.rdiot.yx485.bean
+
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class DeviceStatus(
+    @SerialName("status")
+    var status: Int = 0, // 0
+) {
+    fun postValue(data: DeviceStatus) {
+            TODO("Not yet implemented")
+    }
+}

+ 9 - 2
app/src/main/java/com/rdiot/yx485/bean/RoomData.kt

@@ -49,7 +49,7 @@ data class RoomData(
     var power: Boolean = false, // 0
     var power: Boolean = false, // 0
     /** 记录id */
     /** 记录id */
     @SerialName("record_id")
     @SerialName("record_id")
-    var recordId: String = "", // 1t7svi039tdcoqd4p4g2ncy100mvdglz
+    var recordId: String = "", //
     /** 当前设置的温度 */
     /** 当前设置的温度 */
     @SerialName("set_temp")
     @SerialName("set_temp")
     var setTemp: Int = 25, // 0
     var setTemp: Int = 25, // 0
@@ -73,6 +73,10 @@ data class RoomData(
     var userId: String = "",
     var userId: String = "",
     @SerialName("code")
     @SerialName("code")
     var code: String = "",
     var code: String = "",
+    @SerialName("device_type_name")
+    var deviceTypeName: String = "",
+    @SerialName("is_infrared")
+    var isInfrared: Boolean = false,
     @SerialName("icon")
     @SerialName("icon")
     var icon: String = "",
     var icon: String = "",
 
 
@@ -80,7 +84,10 @@ data class RoomData(
     var isEditMode: Boolean = false,
     var isEditMode: Boolean = false,
 
 
     /** 这个值是房间的,但是每个设备都需要判断,然后展示是否在线,所以在这加一个字段 */
     /** 这个值是房间的,但是每个设备都需要判断,然后展示是否在线,所以在这加一个字段 */
-    var isOnline: Boolean = false
+    @SerialName("is_online")
+    var isOnline: Boolean = false,
+    @SerialName("online_str")
+    var onlineStr: String = "已离线",
 ) : java.io.Serializable {
 ) : java.io.Serializable {
 
 
     companion object {
     companion object {

+ 29 - 1
app/src/main/java/com/rdiot/yx485/net/Api.kt

@@ -18,7 +18,7 @@ import java.io.File
  */
  */
 object Api {
 object Api {
     /** Api地址 */
     /** Api地址 */
-    const val TEST_HOST = "http://yongxu.yehaoji.cn:8199/api/v1/"
+    const val TEST_HOST = "http://113.128.186.214:8199/api/v1/"
 //    const val TEST_HOST = "https://app.yongxulvjian.com/api/v1/"
 //    const val TEST_HOST = "https://app.yongxulvjian.com/api/v1/"
 
 
     /** 头像地址 */
     /** 头像地址 */
@@ -147,6 +147,8 @@ object Api {
 
 
         /** 分控风速 */
         /** 分控风速 */
         const val SUB_WIND_SPEED = "${BASE}/fan_value"
         const val SUB_WIND_SPEED = "${BASE}/fan_value"
+
+        const val DEVICE_ONLINE = "${BASE}/online/%s"
     }
     }
 
 
     object File {
     object File {
@@ -346,6 +348,17 @@ fun CoroutineScope.addRoom(roomName: String, ctrlNum: String, recordId: String)
         )
         )
     }
     }
 
 
+fun CoroutineScope.addMasterRoom(roomName: String, ctrlNum: String, recordId: String) =
+    Post<BaseResp<String>>(Api.Room.BASE) {
+        json(
+            "is_master" to 1,
+            "name" to roomName,
+            "home_id" to LocalData.selFamilyId.value,
+            "control_number" to ctrlNum,
+            "device_type_id" to recordId
+        )
+    }
+
 /** 删除房间 */
 /** 删除房间 */
 fun CoroutineScope.delRoom(roomArray: List<RoomData>) = Delete<BaseResp<String>>(Api.Room.BASE) {
 fun CoroutineScope.delRoom(roomArray: List<RoomData>) = Delete<BaseResp<String>>(Api.Room.BASE) {
     val idArray = arrayListOf<String>()
     val idArray = arrayListOf<String>()
@@ -378,6 +391,7 @@ fun CoroutineScope.getRoomInfo(roomId: String) =
 fun CoroutineScope.setPower(gateway: String, roomData: RoomData) =
 fun CoroutineScope.setPower(gateway: String, roomData: RoomData) =
     Post<BaseResp<String>>(if (RoomUtils.isMaster(roomData.code)) Api.Ctrl.POWER else Api.Ctrl.SUB_POWER) {
     Post<BaseResp<String>>(if (RoomUtils.isMaster(roomData.code)) Api.Ctrl.POWER else Api.Ctrl.SUB_POWER) {
         json(
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "control_number" to roomData.controlNumber,
             "power" to roomData.power
             "power" to roomData.power
@@ -417,12 +431,17 @@ enum class DeviceCode(val typeCode: String) {
 
 
     /** 氟系统-分控 */
     /** 氟系统-分控 */
     FLUORINE_SUB("YXK-F/86-FG-A"),
     FLUORINE_SUB("YXK-F/86-FG-A"),
+
+    INFRARED("INFRARED"),
+
+    DRAUGHT_DISTRIBUTING_BOX("YXK-Z/86-FFX"),
 }
 }
 
 
 /** 设置模式 */
 /** 设置模式 */
 fun CoroutineScope.setMode(gateway: String, roomData: RoomData, modeType: ModeType) =
 fun CoroutineScope.setMode(gateway: String, roomData: RoomData, modeType: ModeType) =
     Post<BaseResp<String>>(Api.Ctrl.MODE) {
     Post<BaseResp<String>>(Api.Ctrl.MODE) {
         json(
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "control_number" to roomData.controlNumber,
             "mode" to modeType.typeCode
             "mode" to modeType.typeCode
@@ -432,6 +451,7 @@ fun CoroutineScope.setMode(gateway: String, roomData: RoomData, modeType: ModeTy
 fun CoroutineScope.setFreshAir(gateway: String, roomData: RoomData) =
 fun CoroutineScope.setFreshAir(gateway: String, roomData: RoomData) =
     Post<BaseResp<String>>(Api.Ctrl.FRESH_AIR) {
     Post<BaseResp<String>>(Api.Ctrl.FRESH_AIR) {
         json(
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "control_number" to roomData.controlNumber,
             "power" to roomData.freshAir
             "power" to roomData.freshAir
@@ -455,6 +475,7 @@ fun CoroutineScope.setTimer(
     time: Float? = null
     time: Float? = null
 ) = Post<BaseResp<String>>(Api.Ctrl.TIMER) {
 ) = Post<BaseResp<String>>(Api.Ctrl.TIMER) {
     json(
     json(
+        "record_id" to roomData.recordId,
         "gateway" to gateway,
         "gateway" to gateway,
         "control_number" to roomData.controlNumber,
         "control_number" to roomData.controlNumber,
         "timer_status" to enable,
         "timer_status" to enable,
@@ -466,6 +487,7 @@ fun CoroutineScope.setTimer(
 fun CoroutineScope.setTemp(gateway: String, roomData: RoomData, temp: Int) =
 fun CoroutineScope.setTemp(gateway: String, roomData: RoomData, temp: Int) =
     Post<BaseResp<String>>(Api.Ctrl.TEMP) {
     Post<BaseResp<String>>(Api.Ctrl.TEMP) {
         json(
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "control_number" to roomData.controlNumber,
             "temp" to temp
             "temp" to temp
@@ -481,6 +503,7 @@ fun CoroutineScope.setWindSpeed(
 ) =
 ) =
     Post<BaseResp<String>>(if (isMaster) Api.Ctrl.WIND_SPEED else Api.Ctrl.SUB_WIND_SPEED) {
     Post<BaseResp<String>>(if (isMaster) Api.Ctrl.WIND_SPEED else Api.Ctrl.SUB_WIND_SPEED) {
         json(
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "control_number" to roomData.controlNumber,
             "fan_value" to speed,
             "fan_value" to speed,
@@ -488,6 +511,11 @@ fun CoroutineScope.setWindSpeed(
         )
         )
     }
     }
 
 
+
+fun CoroutineScope.getDeviceStatus(id: String? = null) =
+    Get<DeviceStatus>(String.format(Api.Ctrl.DEVICE_ONLINE,  id))
+
+
 /** 获取设备类型列表 */
 /** 获取设备类型列表 */
 fun CoroutineScope.getDeviceTypeList() =
 fun CoroutineScope.getDeviceTypeList() =
     Get<List<DeviceTypeData>>(String.format(Api.Device.GET_DEVICE_TYPES))
     Get<List<DeviceTypeData>>(String.format(Api.Device.GET_DEVICE_TYPES))

+ 24 - 1
app/src/main/java/com/rdiot/yx485/ui/bind/BindActivity.kt

@@ -172,7 +172,11 @@ class BindActivity : BaseActivity<ActBindBinding>() {
         checkSwitch()
         checkSwitch()
         if (!isAskingPermission.getAndSet(true)) {
         if (!isAskingPermission.getAndSet(true)) {
             LogUtils.d("请求权限")
             LogUtils.d("请求权限")
-            askPermission(true, callBack)
+            if (LocalData.firstBle==1){
+                showHint()
+            }else {
+                askPermission(true, callBack)
+            }
         }
         }
     }
     }
 
 
@@ -224,6 +228,25 @@ class BindActivity : BaseActivity<ActBindBinding>() {
         }
         }
     }
     }
 
 
+    private fun showHint() {
+        LocalData.firstBle = 0
+        dialog?.dismiss()
+        dialog = IAlertDialog.build(this)
+            .setTitle("位置、蓝牙权限获取说明")
+            .setMessage("用于获取当前位置信息、打开蓝牙以发现设备等场景")
+            .setNegButtonText(R.string.cancel)
+            .setCancelOutside(false)
+            .setNegClickListener {
+                return@setNegClickListener
+            }
+            .setAlpha(1.0F)
+//            .setPosButtonText(R.string.go_setting)
+            .setPosClickListener {
+                askPermission(true, callBack)
+            }
+        dialog?.show()
+    }
+
     private fun showNeedPermissionDialog() {
     private fun showNeedPermissionDialog() {
         if (dialog?.isVisible != true) {
         if (dialog?.isVisible != true) {
             dialog?.dismiss()
             dialog?.dismiss()

+ 44 - 5
app/src/main/java/com/rdiot/yx485/ui/bind/BindDeviceFragment.kt

@@ -20,6 +20,8 @@ import com.rdiot.yx485.base.BaseFragment
 import com.rdiot.yx485.base.BindModelFactory
 import com.rdiot.yx485.base.BindModelFactory
 import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.databinding.FraBindDeviceBinding
 import com.rdiot.yx485.databinding.FraBindDeviceBinding
+import com.rdiot.yx485.net.DeviceCode
+import com.rdiot.yx485.net.addMasterRoom
 import com.rdiot.yx485.net.addRoom
 import com.rdiot.yx485.net.addRoom
 import com.rdiot.yx485.net.updateFamilyData
 import com.rdiot.yx485.net.updateFamilyData
 import com.rdiot.yx485.ui.bind.ble.BleCtrl
 import com.rdiot.yx485.ui.bind.ble.BleCtrl
@@ -51,6 +53,10 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
         bindViewModel.startCountDown()
         bindViewModel.startCountDown()
         startRingAnimation()
         startRingAnimation()
 
 
+        if (bindViewModel.isBindInfrared){
+            bindViewModel.startCheckOnline()
+        }
+
         if (bindViewModel.isBindSub) {
         if (bindViewModel.isBindSub) {
             startBindSubDevice()
             startBindSubDevice()
         } else {
         } else {
@@ -67,6 +73,11 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
                 BleCtrl.cancelBind()
                 BleCtrl.cancelBind()
             }
             }
         }
         }
+        bindViewModel.deiceStatus.observe(this){
+            if (it == 1) {
+                bindSuccess()
+            }
+        }
     }
     }
 
 
     /** 开始绑定分控 */
     /** 开始绑定分控 */
@@ -98,19 +109,35 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
             LogUtils.i(evenString)
             LogUtils.i(evenString)
             binding.tvDebug.text = "${binding.tvDebug.text}$evenString\n"
             binding.tvDebug.text = "${binding.tvDebug.text}$evenString\n"
 
 
+            if (bindViewModel.code ==  DeviceCode.INFRARED.typeCode) {
+                bindViewModel.panelNum.set(e.getId())
+            }
+//            //添加成功
+            if (e.getStep() == 5 && e.getCode() == 0) {
+//                if (bindViewModel.code ==  DeviceCode.INFRARED.typeCode) {
+//                    scopeNetLife {
+//                        addMasterRoom(
+//                            bindViewModel.roomName.get()!!,
+//                            e.getId()  ,
+//                            bindViewModel.deviceTypeId.get()!!
+//                        ).await()
+//                        delay(1500)
+//                    }
+//                }
+                bindSuccess()
+            }
+
+
 
 
             if (!e.isSuccess()) {
             if (!e.isSuccess()) {
                 showErrEventDialog(e.getCode())
                 showErrEventDialog(e.getCode())
                 BleCtrl.cancelBind()
                 BleCtrl.cancelBind()
                 bindViewModel.stopCountDown()
                 bindViewModel.stopCountDown()
+                bindViewModel.stopCheckOnline()
             } else {
             } else {
                 binding.tvSearch.setText(R.string.device_adding)
                 binding.tvSearch.setText(R.string.device_adding)
             }
             }
 
 
-            //添加成功
-            if (e.getStep() == 5 && e.getCode() == 0) {
-                bindSuccess()
-            }
         }
         }
 
 
         lifecycle.addObserver(BleCtrl)
         lifecycle.addObserver(BleCtrl)
@@ -118,7 +145,7 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
 
 
     private fun bindSuccess() {
     private fun bindSuccess() {
         bindViewModel.stopCountDown()
         bindViewModel.stopCountDown()
-
+        bindViewModel.stopCheckOnline()
         if (!bindViewModel.isBindSub) {
         if (!bindViewModel.isBindSub) {
             //更新定位信息
             //更新定位信息
             val familyData = LocalData.familyData.value!!
             val familyData = LocalData.familyData.value!!
@@ -134,6 +161,18 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
             binding.tvDebug.text = getString(R.string.add_finish)
             binding.tvDebug.text = getString(R.string.add_finish)
             BleCtrl.cancelBind()
             BleCtrl.cancelBind()
         }
         }
+
+        if (bindViewModel.code ==  DeviceCode.INFRARED.typeCode) {
+            scopeNetLife {
+                addMasterRoom(
+                    bindViewModel.roomName.get()!!,
+                    bindViewModel.panelNum.get()!!  ,
+                    bindViewModel.deviceTypeId.get()!!
+                ).await()
+                delay(1500)
+            }
+        }
+
         binding.btnNext.setClickLimitListener { act.finish() }
         binding.btnNext.setClickLimitListener { act.finish() }
     }
     }
 
 

+ 29 - 1
app/src/main/java/com/rdiot/yx485/ui/bind/EnterRoomInfoFragment.kt

@@ -6,6 +6,7 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import android.view.ViewGroup
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.databinding.ObservableField
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.findNavController
 import com.drake.net.utils.TipUtils
 import com.drake.net.utils.TipUtils
@@ -17,6 +18,7 @@ import com.rdiot.yx485.base.BindModelFactory
 import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.databinding.FraEnterRoomInfoBinding
 import com.rdiot.yx485.databinding.FraEnterRoomInfoBinding
 import com.rdiot.yx485.ui.bind.model.BindViewModel
 import com.rdiot.yx485.ui.bind.model.BindViewModel
+import com.rdiot.yx485.util.RoomUtils
 import com.rdiot.yx485.util.askPermission
 import com.rdiot.yx485.util.askPermission
 import com.rdiot.yx485.util.goToAppSetting
 import com.rdiot.yx485.util.goToAppSetting
 import com.rdiot.yx485.util.setClickLimitListener
 import com.rdiot.yx485.util.setClickLimitListener
@@ -56,6 +58,9 @@ class EnterRoomInfoFragment : BaseFragment<FraEnterRoomInfoBinding>() {
     ) {
     ) {
         binding.vm = bindViewModel
         binding.vm = bindViewModel
         binding.familyData = LocalData.familyData.value
         binding.familyData = LocalData.familyData.value
+        if (bindViewModel.isBindInfrared) {
+            bindViewModel.panelNum=  ObservableField("ESP_GATTS_DEMO")
+        }
         binding.btnNext.setClickLimitListener {
         binding.btnNext.setClickLimitListener {
             if (bindViewModel.roomName.get().isNullOrBlank()
             if (bindViewModel.roomName.get().isNullOrBlank()
                 || bindViewModel.panelNum.get().isNullOrBlank()
                 || bindViewModel.panelNum.get().isNullOrBlank()
@@ -73,7 +78,11 @@ class EnterRoomInfoFragment : BaseFragment<FraEnterRoomInfoBinding>() {
         }
         }
 
 
         binding.ivScan.setClickLimitListener {
         binding.ivScan.setClickLimitListener {
-            askPermission()
+            if (LocalData.firstCamera) {
+                showHint()
+            }else {
+                askPermission()
+            }
         }
         }
 
 
     }
     }
@@ -92,6 +101,25 @@ class EnterRoomInfoFragment : BaseFragment<FraEnterRoomInfoBinding>() {
         }
         }
     }
     }
 
 
+    private fun showHint() {
+        LocalData.firstCamera = false
+        dialog?.dismiss()
+        dialog = IAlertDialog.build(this)
+            .setTitle("相机权限使用说明")
+            .setMessage("用于拍照、录制视频、扫描二维码等场景")
+            .setNegButtonText(R.string.cancel)
+            .setCancelOutside(false)
+            .setNegClickListener {
+                return@setNegClickListener
+            }
+            .setAlpha(1.0F)
+//            .setPosButtonText(R.string.go_setting)
+            .setPosClickListener {
+                askPermission()
+            }
+        dialog?.show()
+    }
+
     private fun showNeedPermissionDialog(allGranted: Boolean?) {
     private fun showNeedPermissionDialog(allGranted: Boolean?) {
         if (dialog?.isVisible != true) {
         if (dialog?.isVisible != true) {
             dialog?.dismiss()
             dialog?.dismiss()

+ 6 - 6
app/src/main/java/com/rdiot/yx485/ui/bind/SelectDeviceTypeFragment.kt

@@ -2,7 +2,6 @@ package com.rdiot.yx485.ui.bind
 
 
 import android.os.Bundle
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.findNavController
@@ -45,13 +44,14 @@ class SelectDeviceTypeFragment : BaseFragment<FraSelectDeviceTypeBinding>() {
         list = ArrayList()
         list = ArrayList()
         pagerAdapter = SelectDeviceTypeAdapter(context, list, object : OnItemClickListener {
         pagerAdapter = SelectDeviceTypeAdapter(context, list, object : OnItemClickListener {
             override fun onClick(position: Int, deviceTypeData: DeviceTypeData?) {
             override fun onClick(position: Int, deviceTypeData: DeviceTypeData?) {
-                if ((deviceTypeData?.code == DeviceCode.WATER_INTELLIGENCE.typeCode)){
-                    showNoticeDialog()
-                    return
-                }
+//                if ((deviceTypeData?.code == DeviceCode.WATER_INTELLIGENCE.typeCode)){
+//                    showNoticeDialog()
+//                    return
+//                }
                 bindViewModel.deviceTypeId.set(deviceTypeData?.recordId)
                 bindViewModel.deviceTypeId.set(deviceTypeData?.recordId)
                 bindViewModel.isBindSub = !RoomUtils.isMaster(deviceTypeData?.code)
                 bindViewModel.isBindSub = !RoomUtils.isMaster(deviceTypeData?.code)
-
+                bindViewModel.isBindInfrared = RoomUtils.isInfrared(deviceTypeData?.code)
+                bindViewModel.code = deviceTypeData?.code.toString()
                 findNavController().navigate(R.id.action_selectDeviceTypeFragment_to_resetFragment)
                 findNavController().navigate(R.id.action_selectDeviceTypeFragment_to_resetFragment)
             }
             }
         })
         })

+ 3 - 1
app/src/main/java/com/rdiot/yx485/ui/bind/ble/BindBean.kt

@@ -9,5 +9,7 @@ data class BindBean(
     @SerialName("c")
     @SerialName("c")
     var c: Int = 0, // 0
     var c: Int = 0, // 0
     @SerialName("s")
     @SerialName("s")
-    var s: Int = 0 // 4
+    var s: Int = 0, // 4
+    @SerialName("id")
+    var id: String = ""
 )
 )

+ 26 - 8
app/src/main/java/com/rdiot/yx485/ui/bind/ble/BleCtrl.kt

@@ -7,6 +7,7 @@ import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.lifecycleScope
 import com.clj.fastble.BleManager
 import com.clj.fastble.BleManager
 import com.clj.fastble.callback.BleGattCallback
 import com.clj.fastble.callback.BleGattCallback
+import com.clj.fastble.callback.BleMtuChangedCallback
 import com.clj.fastble.callback.BleNotifyCallback
 import com.clj.fastble.callback.BleNotifyCallback
 import com.clj.fastble.callback.BleScanCallback
 import com.clj.fastble.callback.BleScanCallback
 import com.clj.fastble.callback.BleWriteCallback
 import com.clj.fastble.callback.BleWriteCallback
@@ -64,7 +65,7 @@ object BleCtrl : DefaultLifecycleObserver {
     private var bleDevice: BleDevice? = null
     private var bleDevice: BleDevice? = null
     private var isConnecting = AtomicBoolean(false)
     private var isConnecting = AtomicBoolean(false)
     private var bindJob: Job? = null
     private var bindJob: Job? = null
-
+    private var split:Boolean = true
 
 
     /** 初始化BLE */
     /** 初始化BLE */
     private fun initBleManager() {
     private fun initBleManager() {
@@ -106,9 +107,13 @@ object BleCtrl : DefaultLifecycleObserver {
                     }
                     }
 
 
                     override fun onScanning(bleDevice: BleDevice) {
                     override fun onScanning(bleDevice: BleDevice) {
+                        LogUtils.d("deviceName:${bleDevice.name}")
                         if (!bleDevice.name.isNullOrBlank() && bleDevice.name == deviceName) {
                         if (!bleDevice.name.isNullOrBlank() && bleDevice.name == deviceName) {
-                            LogUtils.i("发现设备-> Mac:${bleDevice.mac}  Name:${bleDevice.name}")
-                            if (!isConnecting.get() && bleDevice.rssi > -70) {
+                            if (bleDevice.name != null && bleDevice.name.toString() != "") {
+                                LogUtils.i("发现设备-> Mac:${bleDevice.mac}  Name:${bleDevice.name} rssi:${bleDevice.rssi}" )
+                            }
+
+                            if (!isConnecting.get() ) {
                                 isConnecting.set(true)
                                 isConnecting.set(true)
                                 startConnect(owner, bleDevice)
                                 startConnect(owner, bleDevice)
                                 BleManager.getInstance().cancelScan()
                                 BleManager.getInstance().cancelScan()
@@ -171,14 +176,12 @@ object BleCtrl : DefaultLifecycleObserver {
                             isConnecting.set(true)
                             isConnecting.set(true)
                             LogUtils.i("开始连接 ${device.name} ${device.mac}")
                             LogUtils.i("开始连接 ${device.name} ${device.mac}")
                         }
                         }
-
                         override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                         override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                             isConnecting.set(false)
                             isConnecting.set(false)
                             LogUtils.e("连接失败(${exception.description}) ${device.name} ${device.mac}")
                             LogUtils.e("连接失败(${exception.description}) ${device.name} ${device.mac}")
                             BleManager.getInstance().disconnect(bleDevice)
                             BleManager.getInstance().disconnect(bleDevice)
                             if (cont.isActive) cont.resume(false)
                             if (cont.isActive) cont.resume(false)
                         }
                         }
-
                         override fun onConnectSuccess(
                         override fun onConnectSuccess(
                             device: BleDevice,
                             device: BleDevice,
                             gatt: BluetoothGatt?,
                             gatt: BluetoothGatt?,
@@ -187,6 +190,15 @@ object BleCtrl : DefaultLifecycleObserver {
                             isConnecting.set(true)
                             isConnecting.set(true)
                             bleDevice = device
                             bleDevice = device
                             if (cont.isActive) cont.resume(true)
                             if (cont.isActive) cont.resume(true)
+                            BleManager.getInstance().setMtu(bleDevice, 500, object : BleMtuChangedCallback() {
+                                override fun onMtuChanged(mtu: Int) {
+                                    LogUtils.d("Mtu设置成功")
+                                }
+                                override fun onSetMTUFailure(exception: BleException) {
+                                    // 设置MTU失败
+                                    LogUtils.d("Mtu设置失败")
+                                }
+                            })
                         }
                         }
 
 
                         override fun onDisConnected(
                         override fun onDisConnected(
@@ -231,6 +243,7 @@ object BleCtrl : DefaultLifecycleObserver {
                                 data.byteArrayToHexStr().replace("00", "").hexToByteArray()
                                 data.byteArrayToHexStr().replace("00", "").hexToByteArray()
                             val dataString = realData.toString(Charset.defaultCharset())
                             val dataString = realData.toString(Charset.defaultCharset())
                             val bindBean = Json.decodeFromString<BindBean>(dataString)
                             val bindBean = Json.decodeFromString<BindBean>(dataString)
+                            LogUtils.d("收到数据 $dataString")
                             if (bindBean.c != 0) {
                             if (bindBean.c != 0) {
                                 cancelBind()
                                 cancelBind()
                                 LogUtils.e("设备绑定失败 ${bindBean.s} - ${bindBean.c}")
                                 LogUtils.e("设备绑定失败 ${bindBean.s} - ${bindBean.c}")
@@ -239,7 +252,7 @@ object BleCtrl : DefaultLifecycleObserver {
                                     sendWifiInfo()
                                     sendWifiInfo()
                                 }
                                 }
                             }
                             }
-                            callBack?.invoke(BLECtrlEvent(bindBean.s to bindBean.c))
+                            callBack?.invoke(BLECtrlEvent(Triple(bindBean.s, bindBean.c,bindBean.id )))
 
 
                         }
                         }
                     })
                     })
@@ -272,8 +285,11 @@ object BleCtrl : DefaultLifecycleObserver {
 
 
     private fun sendData(data: ByteArray) {
     private fun sendData(data: ByteArray) {
         if (BleManager.getInstance().allConnectedDevice.size >= 1) {
         if (BleManager.getInstance().allConnectedDevice.size >= 1) {
+            if (bleDevice?.name == "ESP_GATTS_DEMO"){
+                split = false
+            }
             BleManager.getInstance()
             BleManager.getInstance()
-                .write(bleDevice, SERVICE_UUID, READ_CHA_UUID, data, object : BleWriteCallback() {
+                    .writeData(bleDevice, SERVICE_UUID, READ_CHA_UUID, data,split, object : BleWriteCallback() {
                     override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
                     override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray) {
                         Log.i(
                         Log.i(
                             TAG,
                             TAG,
@@ -310,7 +326,7 @@ object BleCtrl : DefaultLifecycleObserver {
  * first Step
  * first Step
  * second Code
  * second Code
  * */
  * */
-class BLECtrlEvent(private val e: Pair<Int, Int>) {
+class BLECtrlEvent(private val e: Triple<Int, Int,String>) {
     companion object {
     companion object {
         val CONNECTING = 1 to 0
         val CONNECTING = 1 to 0
         val CONNECT_FAILED = 1 to -1001
         val CONNECT_FAILED = 1 to -1001
@@ -324,6 +340,8 @@ class BLECtrlEvent(private val e: Pair<Int, Int>) {
     fun getCode() = e.second
     fun getCode() = e.second
 
 
 
 
+    /** 获取Id */
+    fun getId() = e.third
     /** 是否成功 */
     /** 是否成功 */
     fun isSuccess() = this.getCode() == 0
     fun isSuccess() = this.getCode() == 0
 }
 }

+ 37 - 1
app/src/main/java/com/rdiot/yx485/ui/bind/model/BindViewModel.kt

@@ -3,8 +3,16 @@ package com.rdiot.yx485.ui.bind.model
 import androidx.databinding.ObservableField
 import androidx.databinding.ObservableField
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.scopeNetLife
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
+import com.drake.net.utils.scopeNetLife
+import com.drake.serialize.serialize.serialLazy
+import com.drake.serialize.serialize.serialLiveData
 import com.king.zxing.util.LogUtils
 import com.king.zxing.util.LogUtils
+import com.rdiot.yx485.bean.DeviceStatus
+import com.rdiot.yx485.bean.LoginData
+import com.rdiot.yx485.bean.UserData
+import com.rdiot.yx485.net.getDeviceStatus
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.isActive
@@ -31,6 +39,9 @@ class BindViewModel : ViewModel() {
     /** 绑定分控标志 */
     /** 绑定分控标志 */
     var isBindSub: Boolean = false
     var isBindSub: Boolean = false
 
 
+    /** 绑定分控标志 */
+    var isBindInfrared: Boolean = false
+
     /** 定位名字(市) */
     /** 定位名字(市) */
     var locationName: String? = null
     var locationName: String? = null
 
 
@@ -42,9 +53,13 @@ class BindViewModel : ViewModel() {
 
 
     /** 配网倒计时 */
     /** 配网倒计时 */
     var countDownTime: MutableLiveData<Int> = MutableLiveData(-1)
     var countDownTime: MutableLiveData<Int> = MutableLiveData(-1)
-    private var countDownJob: Job? = null
 
 
+    var deiceStatus: MutableLiveData<Int>  = MutableLiveData(0)
+
+    private var countDownJob: Job? = null
+    private var checkOnLine: Job? = null
     companion object {
     companion object {
+        lateinit var DeviceStatus: DeviceStatus
         const val COUNT_TIME = 120
         const val COUNT_TIME = 120
     }
     }
 
 
@@ -81,4 +96,25 @@ class BindViewModel : ViewModel() {
         countDownJob?.cancel()
         countDownJob?.cancel()
         countDownJob = null
         countDownJob = null
     }
     }
+
+    fun startCheckOnline() {
+        checkOnLine?.cancel()
+        countDownTime.value = COUNT_TIME
+        checkOnLine = viewModelScope.launch {
+            while (isActive) {
+                scopeNetLife {
+                    LogUtils.d("panelNum ${panelNum.get()}")
+                        BindViewModel.DeviceStatus = getDeviceStatus(panelNum.get()).await()
+                        deiceStatus.value = BindViewModel.DeviceStatus.status
+                }
+                delay(1000)
+            }
+        }
+    }
+
+    fun stopCheckOnline() {
+        checkOnLine?.cancel()
+        countDownJob = null
+    }
+
 }
 }

+ 25 - 0
app/src/main/java/com/rdiot/yx485/ui/ctrl/RoomCtrlMainActivity.kt

@@ -10,7 +10,9 @@ import com.rdiot.yx485.base.BaseActivity
 import com.rdiot.yx485.bean.RoomData
 import com.rdiot.yx485.bean.RoomData
 import com.rdiot.yx485.databinding.ActRoomCtrlMainBinding
 import com.rdiot.yx485.databinding.ActRoomCtrlMainBinding
 import com.rdiot.yx485.ui.ctrl.model.RoomCtrlViewModel
 import com.rdiot.yx485.ui.ctrl.model.RoomCtrlViewModel
+import com.rdiot.yx485.ui.web.WebActivity
 import com.rdiot.yx485.util.RoomUtils
 import com.rdiot.yx485.util.RoomUtils
+import com.rdiot.yx485.util.clearGoTo
 
 
 /**
 /**
  * 房间控制 容器
  * 房间控制 容器
@@ -27,6 +29,7 @@ class RoomCtrlMainActivity : BaseActivity<ActRoomCtrlMainBinding>() {
     }
     }
 
 
     override fun initView(savedInstanceState: Bundle?) {
     override fun initView(savedInstanceState: Bundle?) {
+
         immersionBar {
         immersionBar {
             statusBarView(binding.v)
             statusBarView(binding.v)
             statusBarColor(R.color.transparent)
             statusBarColor(R.color.transparent)
@@ -41,6 +44,28 @@ class RoomCtrlMainActivity : BaseActivity<ActRoomCtrlMainBinding>() {
         roomCtrlViewModel.roomData.postValue(tempRoomData as RoomData)
         roomCtrlViewModel.roomData.postValue(tempRoomData as RoomData)
         val isMaster = RoomUtils
         val isMaster = RoomUtils
 
 
+        if (RoomUtils.isInfrared(tempRoomData.code)) {
+            finish()
+            clearGoTo(
+                WebActivity.getNewWebIntent(
+                    this,
+                    "https://app.yongxulvjian.com/#/deviceManage/${tempRoomData.recordId}/${tempRoomData.code}",
+                    ""
+                )
+            )
+        }
+
+        if (RoomUtils.isDRAUGHT(tempRoomData.code)) {
+            finish()
+            clearGoTo(
+                WebActivity.getNewWebIntent(
+                    this,
+                    "https://app.yongxulvjian.com/#/airDistributionBox/${tempRoomData.recordId}",
+                    ""
+                )
+            )
+        }
+
         if (!RoomUtils.isMaster(tempRoomData.code)) {
         if (!RoomUtils.isMaster(tempRoomData.code)) {
             navController.navigate(R.id.subRoomCtrlFragment)
             navController.navigate(R.id.subRoomCtrlFragment)
         }
         }

+ 1 - 1
app/src/main/java/com/rdiot/yx485/ui/ctrl/SubRoomCtrlFragment.kt

@@ -181,7 +181,7 @@ class SubRoomCtrlFragment : BaseFragment<FraSubRoomCtrlBinding>(), ClickLimitLis
     //跳转到数据曲线页面
     //跳转到数据曲线页面
     private fun goDataChart(typeName: String, type: String) {
     private fun goDataChart(typeName: String, type: String) {
         act.clearGoTo(
         act.clearGoTo(
-            WebActivity.getNewWebIntent(
+          WebActivity.getNewWebIntent(
                 act,
                 act,
                 "https://app.yongxulvjian.com/#/DataChart/${viewModel.roomData.value?.recordId}/${type}",
                 "https://app.yongxulvjian.com/#/DataChart/${viewModel.roomData.value?.recordId}/${type}",
                 typeName
                 typeName

+ 7 - 7
app/src/main/java/com/rdiot/yx485/ui/home/HomeFragment.kt

@@ -9,7 +9,6 @@ import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
 import androidx.annotation.IdRes
 import androidx.annotation.IdRes
 import com.drake.brv.annotaion.DividerOrientation
 import com.drake.brv.annotaion.DividerOrientation
-import com.drake.brv.utils.bindingAdapter
 import com.drake.brv.utils.divider
 import com.drake.brv.utils.divider
 import com.drake.brv.utils.grid
 import com.drake.brv.utils.grid
 import com.drake.brv.utils.models
 import com.drake.brv.utils.models
@@ -64,10 +63,11 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
             weatherData.now?.let {now->
             weatherData.now?.let {now->
                 // 温度
                 // 温度
                 val ss = SpannableString("${now.temp}℃")
                 val ss = SpannableString("${now.temp}℃")
+                val length = ss.length
                 val sizeSpan = AbsoluteSizeSpan(23, true)
                 val sizeSpan = AbsoluteSizeSpan(23, true)
-                ss.setSpan(sizeSpan, 2, 3, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
+                ss.setSpan(sizeSpan, length-1, length, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
                 val superScriptTextSpan = SuperscriptSpan()
                 val superScriptTextSpan = SuperscriptSpan()
-                ss.setSpan(superScriptTextSpan, 2, 3, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
+                ss.setSpan(superScriptTextSpan, length-1, length, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
                 binding.tvTemp.text = ss
                 binding.tvTemp.text = ss
             }
             }
             binding.weatherData = weatherData
             binding.weatherData = weatherData
@@ -113,7 +113,7 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
         scenesAdapter = ScenesAdapter(act, 20.dp, OnSceneItemClickListener)
         scenesAdapter = ScenesAdapter(act, 20.dp, OnSceneItemClickListener)
         binding.pageView.setAdapter(scenesAdapter)
         binding.pageView.setAdapter(scenesAdapter)
         scenesAdapter.updateAll(list.toList())
         scenesAdapter.updateAll(list.toList())
-
+    
     }
     }
 
 
 
 
@@ -168,9 +168,9 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
                 } else {
                 } else {
                     tempList.clear()
                     tempList.clear()
                     tempList.addAll(roomData)
                     tempList.addAll(roomData)
-                    tempList.forEach {
-                        it.isOnline = familyData.isOnline
-                    }
+//                    tempList.forEach {
+//                        it.isOnline = familyData.isOnline
+//                    }
                     binding.rv.models = tempList
                     binding.rv.models = tempList
 
 
                     binding.ivNoRoom.visibility = View.GONE
                     binding.ivNoRoom.visibility = View.GONE

+ 1 - 0
app/src/main/java/com/rdiot/yx485/ui/login/LoginPwdFragment.kt

@@ -15,6 +15,7 @@ import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.base.LoginViewModelFactory
 import com.rdiot.yx485.base.LoginViewModelFactory
 import com.rdiot.yx485.databinding.FraLoginPwdBinding
 import com.rdiot.yx485.databinding.FraLoginPwdBinding
 import com.rdiot.yx485.net.loginViaPwd
 import com.rdiot.yx485.net.loginViaPwd
+import com.rdiot.yx485.ui.bind.BindActivity
 import com.rdiot.yx485.ui.login.model.LoginRecord
 import com.rdiot.yx485.ui.login.model.LoginRecord
 import com.rdiot.yx485.ui.login.model.LoginType
 import com.rdiot.yx485.ui.login.model.LoginType
 import com.rdiot.yx485.ui.login.model.LoginViewModel
 import com.rdiot.yx485.ui.login.model.LoginViewModel

+ 2 - 0
app/src/main/java/com/rdiot/yx485/ui/main/MainActivity.kt

@@ -2,6 +2,7 @@ package com.rdiot.yx485.ui.main
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
 import android.os.Bundle
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.Fragment
@@ -24,6 +25,7 @@ import com.rdiot.yx485.net.getFamilyData
 import com.rdiot.yx485.net.getWeather
 import com.rdiot.yx485.net.getWeather
 import com.rdiot.yx485.net.getWelcome
 import com.rdiot.yx485.net.getWelcome
 import com.rdiot.yx485.ui.bind.BindActivity
 import com.rdiot.yx485.ui.bind.BindActivity
+import com.rdiot.yx485.ui.find.FindFragment
 import com.rdiot.yx485.ui.home.HomeFragment
 import com.rdiot.yx485.ui.home.HomeFragment
 import com.rdiot.yx485.ui.login.LoginActivity
 import com.rdiot.yx485.ui.login.LoginActivity
 import com.rdiot.yx485.ui.login.SetPwdActivity
 import com.rdiot.yx485.ui.login.SetPwdActivity

+ 1 - 1
app/src/main/java/com/rdiot/yx485/ui/mine/MineFragment.kt

@@ -93,7 +93,7 @@ class MineFragment : BaseFragment<FraMineBinding>() {
                     act.clearGoTo(
                     act.clearGoTo(
                         WebActivity.getNewWebIntent(
                         WebActivity.getNewWebIntent(
                             act,
                             act,
-                            "https://app.yongxulvjian.com/HelpList",
+                            "https://app.yongxulvjian.com/#/HelpList",
                             "帮助列表"
                             "帮助列表"
                         )
                         )
                     )
                     )

+ 31 - 1
app/src/main/java/com/rdiot/yx485/ui/mine/UserInfoActivity.kt

@@ -2,11 +2,16 @@ package com.rdiot.yx485.ui.mine
 
 
 import android.content.Context
 import android.content.Context
 import android.content.Intent
 import android.content.Intent
+import android.graphics.Color
 import android.net.Uri
 import android.net.Uri
 import android.os.Bundle
 import android.os.Bundle
+import android.view.Gravity
 import android.view.View
 import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
 import android.view.inputmethod.InputMethodManager
 import android.view.inputmethod.InputMethodManager
 import androidx.appcompat.widget.AppCompatEditText
 import androidx.appcompat.widget.AppCompatEditText
+import androidx.core.content.ContextCompat
 import androidx.core.widget.doAfterTextChanged
 import androidx.core.widget.doAfterTextChanged
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.lifecycleScope
 import com.drake.net.component.Progress
 import com.drake.net.component.Progress
@@ -16,7 +21,9 @@ import com.github.gzuliyujiang.imagepicker.ActivityBuilder
 import com.github.gzuliyujiang.imagepicker.CropImageView
 import com.github.gzuliyujiang.imagepicker.CropImageView
 import com.github.gzuliyujiang.imagepicker.ImagePicker
 import com.github.gzuliyujiang.imagepicker.ImagePicker
 import com.github.gzuliyujiang.imagepicker.PickCallback
 import com.github.gzuliyujiang.imagepicker.PickCallback
+import com.google.android.material.snackbar.Snackbar
 import com.gyf.immersionbar.ktx.immersionBar
 import com.gyf.immersionbar.ktx.immersionBar
+import com.gyf.immersionbar.ktx.setFitsSystemWindows
 import com.king.zxing.util.LogUtils
 import com.king.zxing.util.LogUtils
 import com.lindroy.iosdialog.IAlertDialog
 import com.lindroy.iosdialog.IAlertDialog
 import com.lindroy.iosdialog.IBottomListDialog
 import com.lindroy.iosdialog.IBottomListDialog
@@ -162,6 +169,25 @@ class UserInfoActivity : BaseActivity<ActUserInfoBinding>() {
         }
         }
     }
     }
 
 
+    private fun showHint() {
+        LocalData.firstCamera = true
+        dialog?.dismiss()
+        dialog = IAlertDialog.build(this)
+            .setTitle("相机权限使用说明")
+            .setMessage("用于拍照、录制视频、扫描二维码等场景")
+            .setNegButtonText(R.string.cancel)
+            .setCancelOutside(false)
+            .setNegClickListener {
+                return@setNegClickListener
+            }
+            .setAlpha(1.0F)
+//            .setPosButtonText(R.string.go_setting)
+            .setPosClickListener {
+                askPermission()
+            }
+        dialog?.show()
+    }
+
     private fun askPermission() {
     private fun askPermission() {
         askPermission(false) { allGranted: Boolean?, _: MutableList<String>? ->
         askPermission(false) { allGranted: Boolean?, _: MutableList<String>? ->
             if (allGranted == true) {
             if (allGranted == true) {
@@ -179,7 +205,11 @@ class UserInfoActivity : BaseActivity<ActUserInfoBinding>() {
             .addItems(listOf(getString(R.string.take_photo), getString(R.string.photo_album)))
             .addItems(listOf(getString(R.string.take_photo), getString(R.string.photo_album)))
             .setOnItemClickListener { position, _, _, _ ->
             .setOnItemClickListener { position, _, _, _ ->
                 if (position == 0) {
                 if (position == 0) {
-                    askPermission()
+                    if (LocalData.firstCamera){
+                        showHint()
+                    }else {
+                        askPermission()
+                    }
                 } else if (position == 1) {
                 } else if (position == 1) {
                     ImagePicker.getInstance().startGallery(this, true, cropCallback)
                     ImagePicker.getInstance().startGallery(this, true, cropCallback)
                 }
                 }

+ 5 - 1
app/src/main/java/com/rdiot/yx485/ui/room/RoomFragment.kt

@@ -26,6 +26,7 @@ import com.rdiot.yx485.net.setPower
 import com.rdiot.yx485.ui.bind.BindActivity
 import com.rdiot.yx485.ui.bind.BindActivity
 import com.rdiot.yx485.ui.ctrl.RoomCtrlMainActivity
 import com.rdiot.yx485.ui.ctrl.RoomCtrlMainActivity
 import com.rdiot.yx485.ui.main.MainActivity
 import com.rdiot.yx485.ui.main.MainActivity
+import com.rdiot.yx485.util.RoomUtils
 import com.rdiot.yx485.util.clearGoTo
 import com.rdiot.yx485.util.clearGoTo
 import com.rdiot.yx485.util.curTime
 import com.rdiot.yx485.util.curTime
 import com.rdiot.yx485.util.getClearTopIntent
 import com.rdiot.yx485.util.getClearTopIntent
@@ -139,7 +140,10 @@ class RoomFragment : BaseFragment<FraRoomBinding>() {
 
 
                     resourceRoomDataList.forEach {
                     resourceRoomDataList.forEach {
                         it.isEditMode = binding.rv.bindingAdapter.toggleMode
                         it.isEditMode = binding.rv.bindingAdapter.toggleMode
-                        it.isOnline = familyData.isOnline
+                        it.isInfrared = RoomUtils.isInfrared(it.code)
+                        if (it.isOnline) {
+                            it.onlineStr = "在线"
+                        }
                     }
                     }
 
 
                     binding.rv.models = resourceRoomDataList
                     binding.rv.models = resourceRoomDataList

+ 122 - 48
app/src/main/java/com/rdiot/yx485/ui/web/WebActivity.kt

@@ -2,17 +2,23 @@ package com.rdiot.yx485.ui.web
 
 
 import android.content.Context
 import android.content.Context
 import android.content.Intent
 import android.content.Intent
+import android.graphics.Bitmap
 import android.os.Bundle
 import android.os.Bundle
+import android.util.Log
 import android.view.KeyEvent
 import android.view.KeyEvent
 import android.view.View
 import android.view.View
 import android.webkit.*
 import android.webkit.*
 import android.webkit.WebSettings.LOAD_NO_CACHE
 import android.webkit.WebSettings.LOAD_NO_CACHE
 import android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
 import android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
+import com.github.lzyzsd.jsbridge.BridgeWebView
+import com.github.lzyzsd.jsbridge.OnBridgeCallback
+import com.google.gson.Gson
 import com.gyf.immersionbar.ktx.immersionBar
 import com.gyf.immersionbar.ktx.immersionBar
 import com.king.zxing.util.LogUtils
 import com.king.zxing.util.LogUtils
 import com.rdiot.yx485.BuildConfig
 import com.rdiot.yx485.BuildConfig
 import com.rdiot.yx485.R
 import com.rdiot.yx485.R
 import com.rdiot.yx485.base.BaseActivity
 import com.rdiot.yx485.base.BaseActivity
+import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.databinding.ActWebBinding
 import com.rdiot.yx485.databinding.ActWebBinding
 
 
 /**
 /**
@@ -49,62 +55,88 @@ class WebActivity : BaseActivity<ActWebBinding>() {
         }
         }
 
 
         webUrl = intent.getStringExtra(WEB_URL) ?: ""
         webUrl = intent.getStringExtra(WEB_URL) ?: ""
-        title = intent.getStringExtra(TITLE) ?: ""
-        binding.nb.setTitle(title)
-        binding.nb.setLeftClickListener {
-            if (binding.wv.canGoBack()) {
-                binding.wv.goBack()
-            } else {
-                finish()
-            }
-        }
+//        title = intent.getStringExtra(TITLE) ?: ""
+//        binding.nb.setTitle(title)
+//        binding.nb.setLeftClickListener {
+//            if (binding.wv.canGoBack()) {
+//                binding.wv.goBack()
+//            } else {
+//                finish()
+//            }
+//        }
         initWebView()
         initWebView()
     }
     }
 
 
     private fun initWebView() {
     private fun initWebView() {
-        binding.wv.apply {
-            webViewClient = object : WebViewClient() {
-                override fun shouldOverrideUrlLoading(
-                    view: WebView?,
-                    request: WebResourceRequest
-                ): Boolean {
-                    return true
+        val webView: BridgeWebView = binding.wv
+        webView.addJavascriptInterface(MainJavascriptInterface(webView.callbacks, webView, this), "WebViewJavascriptBridge")
+        webView.setGson(Gson())
+        webView.apply {
+
+            webView.webViewClient = object : WebViewClient() {
+                override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
+                    super.onPageStarted(view, url, favicon)
+                    binding.wv.visibility = View.GONE
+                    binding.bridgeLayoutMainGif.visibility = View.VISIBLE
                 }
                 }
 
 
+                //
                 override fun onPageFinished(webView: WebView?, s: String?) {
                 override fun onPageFinished(webView: WebView?, s: String?) {
                     super.onPageFinished(webView, s)
                     super.onPageFinished(webView, s)
-                    //如果有问题,会先执行error,
-                    if (isError) {
-                        binding.wv.visibility = View.GONE
-                        binding.bridgeLayoutMainGif.visibility = View.GONE
-                    } else {
-                        binding.wv.visibility = View.VISIBLE
-                        binding.bridgeLayoutMainGif.visibility = View.GONE
-                    }
-                }
-
-                override fun onReceivedError(
-                    webView: WebView?,
-                    webResourceRequest: WebResourceRequest,
-                    webResourceError: WebResourceError?
-                ) {
-                    super.onReceivedError(webView, webResourceRequest, webResourceError)
-                    if (webResourceRequest.isForMainFrame) {
-                        //加载失败后,需要设置isLoadSuccess为false
-                        isError = true
-                    }
+                    binding.wv.visibility = View.VISIBLE
+                    binding.bridgeLayoutMainGif.visibility = View.GONE
                 }
                 }
             }
             }
-            webChromeClient = object : WebChromeClient() {
-                override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
-                    LogUtils.d("consoleMessage:${consoleMessage?.message().toString()}")
-                    return super.onConsoleMessage(consoleMessage)
-                }
 
 
-                override fun onReceivedTitle(view: WebView?, title: String?) {
-                    super.onReceivedTitle(view, title)
-                }
-            }
+
+
+
+
+
+//            webView.webViewClient = object : WebViewClient() {
+//                override fun shouldOverrideUrlLoading(
+//                    view: WebView?,
+//                    request: WebResourceRequest
+//                ): Boolean {
+//                    view?.loadUrl(request.url.toString())
+//                    return true
+//                }
+//
+    //                override fun onPageFinished(webView: WebView?, s: String?) {
+//                    super.onPageFinished(webView, s)
+//                    //如果有问题,会先执行error,
+//                    if (isError) {
+//                        binding.wv.visibility = View.GONE
+//                        binding.bridgeLayoutMainGif.visibility = View.GONE
+//                    } else {
+//                        binding.wv.visibility = View.VISIBLE
+//                        binding.bridgeLayoutMainGif.visibility = View.GONE
+//                    }
+//                }
+//
+//                override fun onReceivedError(
+//                    webView: WebView?,
+//                    webResourceRequest: WebResourceRequest,
+//                    webResourceError: WebResourceError?
+//                ) {
+//                    super.onReceivedError(webView, webResourceRequest, webResourceError)
+//                    if (webResourceRequest.isForMainFrame) {
+//                        //加载失败后,需要设置isLoadSuccess为false
+//                        isError = true
+//                    }
+//                }
+//            }
+            //webView.setDefaultHandler(DefaultHandler())
+//            webView.webChromeClient = object : WebChromeClient() {
+//                override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
+//                    LogUtils.d("consoleMessage:${consoleMessage?.message().toString()}")
+//                    return super.onConsoleMessage(consoleMessage)
+//                }
+//
+//                override fun onReceivedTitle(view: WebView?, title: String?) {
+//                    super.onReceivedTitle(view, title)
+//                }
+//            }
             val userAgent = settings.userAgentString
             val userAgent = settings.userAgentString
 
 
             settings.apply {
             settings.apply {
@@ -122,9 +154,9 @@ class WebActivity : BaseActivity<ActWebBinding>() {
                 domStorageEnabled = true
                 domStorageEnabled = true
                 cacheMode = LOAD_NO_CACHE
                 cacheMode = LOAD_NO_CACHE
             }
             }
-            if (BuildConfig.DEBUG) {
-                WebView.setWebContentsDebuggingEnabled(true)
-            }
+
+            WebView.setWebContentsDebuggingEnabled(true)
+
         }
         }
         binding.wv.loadUrl(webUrl)
         binding.wv.loadUrl(webUrl)
     }
     }
@@ -171,4 +203,46 @@ class WebActivity : BaseActivity<ActWebBinding>() {
         binding.wv.loadUrl("about:blank")
         binding.wv.loadUrl("about:blank")
         binding.wv.destroy()
         binding.wv.destroy()
     }
     }
+}
+
+class MainJavascriptInterface : BridgeWebView.BaseJavascriptInterface {
+    private var mWebView: BridgeWebView? = null
+    private var _context: WebActivity? = null
+    constructor(callbacks: Map<String?, OnBridgeCallback?>?, webView: BridgeWebView?, context: WebActivity) : super(
+        callbacks
+    ) {
+        _context = context
+        mWebView = webView
+    }
+
+    constructor(callbacks: Map<String?, OnBridgeCallback?>?) : super(callbacks)
+
+    override fun send(data: String): String {
+        return "it is default response"
+    }
+
+    @JavascriptInterface
+    fun getCurrentRoomId(data: String, callbackId: String) {
+        Log.d(
+            "MainJavascriptInterface",
+            data + ", callbackId: " + callbackId + " " + Thread.currentThread().name
+        )
+        mWebView!!.sendResponse("{\"responseData\":\"${LocalData.selFamilyId.value}\"}", callbackId)
+    }
+    @JavascriptInterface
+    fun closeWebView(data: String, callbackId: String) {
+        Log.d(
+            "MainJavascriptInterface",
+            data + ", callbackId: " + callbackId + " " + Thread.currentThread().name
+        )
+        _context?.finish();
+    }
+    @JavascriptInterface
+    fun getToken(data: String, callbackId: String) {
+        Log.d(
+            "MainJavascriptInterface",
+            data + ", callbackId: " + callbackId + " " + Thread.currentThread().name
+        )
+        mWebView!!.sendResponse("{\"responseData\":\"${LocalData.loginData?.accessToken}\"}", callbackId)
+    }
 }
 }

+ 42 - 0
app/src/main/java/com/rdiot/yx485/util/RoomUtils.kt

@@ -19,6 +19,7 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.mipmap.water_sub
             DeviceCode.WATER_SUB.typeCode -> R.mipmap.water_sub
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.mipmap.water_intelligence
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.mipmap.water_intelligence
             DeviceCode.FLUORINE_MASTER.typeCode -> R.mipmap.fluorine_master
             DeviceCode.FLUORINE_MASTER.typeCode -> R.mipmap.fluorine_master
+            DeviceCode.INFRARED.typeCode -> R.mipmap.fluorine_master
             else -> R.mipmap.fluorine_sub
             else -> R.mipmap.fluorine_sub
         }
         }
 
 
@@ -64,6 +65,7 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.string.sub_en
             DeviceCode.WATER_SUB.typeCode -> R.string.sub_en
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.string.intelligence_en
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.string.intelligence_en
             DeviceCode.FLUORINE_MASTER.typeCode -> R.string.master_en
             DeviceCode.FLUORINE_MASTER.typeCode -> R.string.master_en
+            DeviceCode.INFRARED.typeCode -> R.string.infrared
             else -> R.string.sub_en
             else -> R.string.sub_en
         }
         }
         return result
         return result
@@ -76,6 +78,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_4AB0E4
             DeviceCode.WATER_SUB.typeCode -> R.color.c_4AB0E4
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_60A736
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_60A736
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_5198FD
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_5198FD
+            DeviceCode.INFRARED.typeCode -> R.color.c_5198FD
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> R.color.c_5198FD
             else -> R.color.c_F0B756
             else -> R.color.c_F0B756
         }
         }
         val shape = GradientDrawable()
         val shape = GradientDrawable()
@@ -91,6 +95,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_387084
             DeviceCode.WATER_SUB.typeCode -> R.color.c_387084
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_517232
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_517232
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_31517D
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_31517D
+            DeviceCode.INFRARED.typeCode -> R.color.c_31517D
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> R.color.c_31517D
             else -> R.color.c_A79365
             else -> R.color.c_A79365
         }
         }
         return ContextCompat.getColor(context, result)
         return ContextCompat.getColor(context, result)
@@ -103,6 +109,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_55DCDF
             DeviceCode.WATER_SUB.typeCode -> R.color.c_55DCDF
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_B1EB5C
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_B1EB5C
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_97C2FF
             DeviceCode.FLUORINE_MASTER.typeCode -> R.color.c_97C2FF
+            DeviceCode.INFRARED.typeCode -> R.color.c_55DCDF
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> R.color.c_97C2FF
             else -> R.color.c_FAE06A
             else -> R.color.c_FAE06A
         }
         }
         val shape = GradientDrawable()
         val shape = GradientDrawable()
@@ -125,6 +133,38 @@ object RoomUtils {
         return result
         return result
     }
     }
 
 
+    //获取蓝牙名称
+    @JvmStatic
+    fun getBleName(code: String?): String {
+        val result = when (code) {
+            DeviceCode.INFRARED.typeCode -> "XIAO_WU"
+            else -> ""
+        }
+        return result
+    }
+
+
+    //是否是红外设备
+    @JvmStatic
+    fun isInfrared(code: String?): Boolean {
+        val result = when (code) {
+            DeviceCode.INFRARED.typeCode -> true
+            else -> false
+        }
+        return result
+    }
+
+    //是否是红外设备
+    @JvmStatic
+    fun isDRAUGHT(code: String?): Boolean {
+        val result = when (code) {
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> true
+            else -> false
+        }
+        return result
+    }
+
+
     //是否是主控
     //是否是主控
     @JvmStatic
     @JvmStatic
     fun isMaster(code: String?): Boolean {
     fun isMaster(code: String?): Boolean {
@@ -133,6 +173,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> false
             DeviceCode.WATER_SUB.typeCode -> false
             DeviceCode.WATER_INTELLIGENCE.typeCode -> false
             DeviceCode.WATER_INTELLIGENCE.typeCode -> false
             DeviceCode.FLUORINE_MASTER.typeCode -> true
             DeviceCode.FLUORINE_MASTER.typeCode -> true
+            DeviceCode.INFRARED.typeCode -> true
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> true
             else -> false
             else -> false
         }
         }
         return result
         return result

+ 5 - 6
app/src/main/res/layout/act_web.xml

@@ -17,17 +17,16 @@
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="0dp" />
             android:layout_height="0dp" />
 
 
-        <com.rdiot.yx485.view.ZNavbar
-            android:id="@+id/nb"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+<!--        <com.rdiot.yx485.view.ZNavbar-->
+<!--            android:id="@+id/nb"-->
+<!--            android:layout_width="match_parent"-->
+<!--            android:layout_height="wrap_content" />-->
 
 
 
 
-        <WebView
+        <com.github.lzyzsd.jsbridge.BridgeWebView
             android:id="@+id/wv"
             android:id="@+id/wv"
             android:background="@color/white"
             android:background="@color/white"
             android:layout_width="match_parent"
             android:layout_width="match_parent"
-            android:visibility="gone"
             android:layout_height="match_parent" />
             android:layout_height="match_parent" />
 
 
 
 

+ 2 - 0
app/src/main/res/layout/fra_enter_room_info.xml

@@ -11,6 +11,7 @@
         <variable
         <variable
             name="vm"
             name="vm"
             type="com.rdiot.yx485.ui.bind.model.BindViewModel" />
             type="com.rdiot.yx485.ui.bind.model.BindViewModel" />
+        <import type="android.view.View" />
     </data>
     </data>
 
 
     <LinearLayout
     <LinearLayout
@@ -73,6 +74,7 @@
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="42dp"
                 android:layout_marginTop="42dp"
                 android:gravity="center"
                 android:gravity="center"
+                android:visibility="@{vm.isBindInfrared?View.GONE:View.VISIBLE}"
                 android:orientation="horizontal">
                 android:orientation="horizontal">
 
 
                 <androidx.appcompat.widget.AppCompatEditText
                 <androidx.appcompat.widget.AppCompatEditText

+ 4 - 3
app/src/main/res/layout/fra_home.xml

@@ -208,9 +208,10 @@
                             <androidx.appcompat.widget.AppCompatImageView
                             <androidx.appcompat.widget.AppCompatImageView
                                 srcString="@{String.format(`icon_weather_%s`,weatherData.now.iconCode)}"
                                 srcString="@{String.format(`icon_weather_%s`,weatherData.now.iconCode)}"
                                 android:id="@+id/iv_weather"
                                 android:id="@+id/iv_weather"
-                                android:layout_width="90dp"
-                                android:layout_height="90dp"
-                                android:layout_marginEnd="10dp"
+                                android:layout_width="80dp"
+                                android:layout_height="80dp"
+                                android:layout_marginEnd="5dp"
+                                android:layout_marginTop="10dp"
                                 app:layout_constraintBottom_toBottomOf="parent"
                                 app:layout_constraintBottom_toBottomOf="parent"
                                 app:layout_constraintEnd_toEndOf="parent"
                                 app:layout_constraintEnd_toEndOf="parent"
                                 app:layout_constraintTop_toTopOf="parent"
                                 app:layout_constraintTop_toTopOf="parent"

+ 1 - 1
app/src/main/res/layout/fra_mine.xml

@@ -79,6 +79,7 @@
 
 
                 </LinearLayout>
                 </LinearLayout>
 
 
+
                 <LinearLayout
                 <LinearLayout
                     android:id="@+id/ll_home"
                     android:id="@+id/ll_home"
                     android:layout_width="match_parent"
                     android:layout_width="match_parent"
@@ -118,7 +119,6 @@
                         app:srcCompat="@mipmap/icon_enter" />
                         app:srcCompat="@mipmap/icon_enter" />
                 </LinearLayout>
                 </LinearLayout>
 
 
-
                 <androidx.constraintlayout.widget.ConstraintLayout
                 <androidx.constraintlayout.widget.ConstraintLayout
                     android:layout_width="match_parent"
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:layout_height="0dp"

+ 44 - 1
app/src/main/res/layout/fra_reset.xml

@@ -19,7 +19,7 @@
         <androidx.core.widget.NestedScrollView
         <androidx.core.widget.NestedScrollView
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_height="0dp"
-            android:visibility="@{vm.bindSub?View.GONE:View.VISIBLE}"
+            android:visibility="@{vm.bindSub||vm.bindInfrared?View.GONE:View.VISIBLE}"
             app:layout_constraintBottom_toTopOf="@id/btn_next"
             app:layout_constraintBottom_toTopOf="@id/btn_next"
             app:layout_constraintTop_toTopOf="parent">
             app:layout_constraintTop_toTopOf="parent">
 
 
@@ -101,6 +101,49 @@
 
 
         </androidx.core.widget.NestedScrollView>
         </androidx.core.widget.NestedScrollView>
 
 
+        <androidx.core.widget.NestedScrollView
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:visibility="@{vm.isBindInfrared?View.VISIBLE:View.GONE}"
+            app:layout_constraintBottom_toTopOf="@id/btn_next"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:visibility="gone">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:orientation="vertical"
+                android:visibility="@{vm.isBindInfrared?View.VISIBLE:View.GONE}">
+
+<!--                <TextView-->
+<!--                    android:layout_width="match_parent"-->
+<!--                    android:layout_height="wrap_content"-->
+<!--                    android:paddingHorizontal="50dp"-->
+<!--                    android:paddingTop="25dp"-->
+<!--                    android:text="@string/reset_tips_3" />-->
+
+                <androidx.appcompat.widget.AppCompatImageView
+                    android:layout_width="250dp"
+                    android:layout_height="250dp"
+                    app:srcCompat="@mipmap/img_infrared_reset" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingHorizontal="50dp"
+                    android:paddingTop="15dp"
+                    android:text="@string/reset_tips_3" />
+
+<!--                <androidx.appcompat.widget.AppCompatImageView-->
+<!--                    android:layout_width="250dp"-->
+<!--                    android:layout_height="250dp"-->
+<!--                    app:srcCompat="@mipmap/img_sub_reset_2" />-->
+
+            </LinearLayout>
+
+
+        </androidx.core.widget.NestedScrollView>
 
 
         <androidx.appcompat.widget.AppCompatButton
         <androidx.appcompat.widget.AppCompatButton
             android:id="@+id/btn_next"
             android:id="@+id/btn_next"

+ 11 - 11
app/src/main/res/layout/item_room.xml

@@ -74,7 +74,7 @@
             <TextView
             <TextView
                 android:layout_width="wrap_content"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@{vm.name}"
+                android:text="@{vm.deviceTypeName}"
                 android:textColor="@color/c_999999"
                 android:textColor="@color/c_999999"
                 android:textSize="12sp"
                 android:textSize="12sp"
                 tools:text="主控制器"
                 tools:text="主控制器"
@@ -94,22 +94,22 @@
                 tools:text="湿度:55%"
                 tools:text="湿度:55%"
                 android:textSize="12sp"
                 android:textSize="12sp"
                 app:drawableStartCompat="@drawable/bg_item_room_humidity_temperature"
                 app:drawableStartCompat="@drawable/bg_item_room_humidity_temperature"
-                android:visibility="@{vm.isOnline?View.VISIBLE:View.GONE}"
+                android:visibility="@{vm.isOnline&amp;!vm.infrared?View.VISIBLE:View.GONE}"
                 tools:textColor="@color/c_14C9C8"
                 tools:textColor="@color/c_14C9C8"
                 android:textColor="@color/c_14C9C8"
                 android:textColor="@color/c_14C9C8"
                 android:layout_marginStart="8dp"/>
                 android:layout_marginStart="8dp"/>
 
 
             <TextView
             <TextView
                 android:layout_width="wrap_content"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/disconnect"
-                android:textColor="@color/c_FAAD14"
-                tools:text="已离线"
-                android:textSize="12sp"
-                android:visibility="@{vm.isOnline?View.GONE:View.VISIBLE}"
-                tools:textColor="@color/c_FAAD14"
+                android:layout_height="18dp"
                 android:layout_marginStart="8dp"
                 android:layout_marginStart="8dp"
-                app:drawableStartCompat="@drawable/bg_item_room_disconnect" />
+                android:text="@{vm.onlineStr}"
+                android:textColor="@{vm.isOnline?@color/c_14C9C8:@color/c_FAAD14}"
+                android:textSize="12sp"
+                android:visibility="@{vm.isOnline&amp;!vm.infrared?View.GONE:View.VISIBLE}"
+                app:drawableStartCompat="@drawable/bg_item_room_disconnect"
+                tools:text="已离线"
+                tools:textColor="@color/c_FAAD14" />
 
 
         </LinearLayout>
         </LinearLayout>
 
 
@@ -120,7 +120,7 @@
             android:layout_width="48dp"
             android:layout_width="48dp"
             android:layout_height="24dp"
             android:layout_height="24dp"
             android:layout_marginEnd="16dp"
             android:layout_marginEnd="16dp"
-            android:visibility="@{vm.editMode?View.GONE:View.VISIBLE}"
+            android:visibility="@{vm.editMode||vm.infrared?View.GONE:View.VISIBLE}"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintTop_toTopOf="parent"

BIN
app/src/main/res/mipmap-xxhdpi/img_infrared_reset.png


+ 5 - 1
app/src/main/res/values/strings.xml

@@ -12,6 +12,8 @@
 
 
     <string name="my_room">我的房间</string>
     <string name="my_room">我的房间</string>
     <string name="no_room">您还没有添加房间</string>
     <string name="no_room">您还没有添加房间</string>
+    <string name="no_device">您还没有添加设备</string>
+    <string name="add_device">添加设备</string>
     <string name="add_room">添加房间</string>
     <string name="add_room">添加房间</string>
     <string name="select_device_type">选择设备类型</string>
     <string name="select_device_type">选择设备类型</string>
     <string name="add">添加</string>
     <string name="add">添加</string>
@@ -23,7 +25,8 @@
     <string name="no_net_refresh">立即刷新</string>
     <string name="no_net_refresh">立即刷新</string>
 
 
     <string name="master_en">main controller</string>
     <string name="master_en">main controller</string>
-    <string name="sub_en">Sub controller</string>
+    <string name="sub_en">sub controller</string>
+    <string name="infrared">infrared</string>
     <string name="intelligence_en">Intelligent water collector</string>
     <string name="intelligence_en">Intelligent water collector</string>
 
 
     <string name="fluorine_system">氟系统</string>
     <string name="fluorine_system">氟系统</string>
@@ -108,6 +111,7 @@
 
 
     <string name="reset_tips_1">1、开机状态下,长按『菜单键』3s进入设置界面</string>
     <string name="reset_tips_1">1、开机状态下,长按『菜单键』3s进入设置界面</string>
     <string name="reset_tips_2">2、再次长按『菜单键』,直到显示配网二维码</string>
     <string name="reset_tips_2">2、再次长按『菜单键』,直到显示配网二维码</string>
+    <string name="reset_tips_3">永续精灵通电状态下,长按 5 秒钟设备中间按钮,语音提示:开始蓝牙配网,蓝色灯为快闪状态,将手机靠近设备开始配网。</string>
     <string name="sub_reset_tips_1">1、开机状态下,长按『菜单键』3s直到界面出现配网二维码</string>
     <string name="sub_reset_tips_1">1、开机状态下,长按『菜单键』3s直到界面出现配网二维码</string>
     <string name="sub_reset_tips_2">2、使用APP扫码绑定</string>
     <string name="sub_reset_tips_2">2、使用APP扫码绑定</string>
     <string name="next_step">下一步</string>
     <string name="next_step">下一步</string>

+ 4 - 5
arcseekbar/build.gradle

@@ -4,13 +4,11 @@ apply plugin: 'com.android.library'
 android {
 android {
 
 
     compileSdkVersion 29
     compileSdkVersion 29
-    buildToolsVersion "29.0.3"
+    buildToolsVersion "30.0.3"
 
 
     defaultConfig {
     defaultConfig {
         minSdkVersion 16
         minSdkVersion 16
         targetSdkVersion 29
         targetSdkVersion 29
-        versionCode 3
-        versionName "1.0.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     }
 
 
@@ -20,12 +18,13 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
         }
     }
     }
-
-    lintOptions {
+    namespace 'com.king.view.arcseekbar'
+    lint {
         abortOnError false
         abortOnError false
         warning 'InvalidPackage'
         warning 'InvalidPackage'
     }
     }
 
 
+
 }
 }
 
 
 dependencies {
 dependencies {

+ 1 - 2
arcseekbar/src/main/AndroidManifest.xml

@@ -1,2 +1 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.king.view.arcseekbar" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

+ 2 - 2
build.gradle

@@ -1,7 +1,7 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 plugins {
 plugins {
-    id 'com.android.application' version '7.1.2' apply false
-    id 'com.android.library' version '7.1.2' apply false
+    id 'com.android.application' version '7.4.2' apply false
+    id 'com.android.library' version '7.4.2' apply false
     id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
     id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
     id 'com.google.dagger.hilt.android' version '2.44' apply false
     id 'com.google.dagger.hilt.android' version '2.44' apply false
     id 'org.jetbrains.kotlin.multiplatform' version '1.7.10' apply false
     id 'org.jetbrains.kotlin.multiplatform' version '1.7.10' apply false

BIN
gradle/wrapper/gradle-wrapper.jar


+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
 #Thu Dec 15 17:20:20 CST 2022
 #Thu Dec 15 17:20:20 CST 2022
 distributionBase=GRADLE_USER_HOME
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME

+ 153 - 104
gradlew

@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
 
 
 #
 #
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
 #
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
 #
 #
 
 
 ##############################################################################
 ##############################################################################
-##
-##  Gradle start up script for UN*X
-##
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
 ##############################################################################
 ##############################################################################
 
 
 # Attempt to set APP_HOME
 # Attempt to set APP_HOME
+
 # Resolve links: $0 may be a link
 # Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
 done
 done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 
 
 APP_NAME="Gradle"
 APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
 
 
 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
 
 
 warn () {
 warn () {
     echo "$*"
     echo "$*"
-}
+} >&2
 
 
 die () {
 die () {
     echo
     echo
     echo "$*"
     echo "$*"
     echo
     echo
     exit 1
     exit 1
-}
+} >&2
 
 
 # OS specific support (must be 'true' or 'false').
 # OS specific support (must be 'true' or 'false').
 cygwin=false
 cygwin=false
 msys=false
 msys=false
 darwin=false
 darwin=false
 nonstop=false
 nonstop=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-  NONSTOP* )
-    nonstop=true
-    ;;
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
 esac
 esac
 
 
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 if [ -n "$JAVA_HOME" ] ; then
 if [ -n "$JAVA_HOME" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
         # IBM's JDK on AIX uses strange locations for the executables
         # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
+        JAVACMD=$JAVA_HOME/jre/sh/java
     else
     else
-        JAVACMD="$JAVA_HOME/bin/java"
+        JAVACMD=$JAVA_HOME/bin/java
     fi
     fi
     if [ ! -x "$JAVACMD" ] ; then
     if [ ! -x "$JAVACMD" ] ; then
         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
 location of your Java installation."
     fi
     fi
 else
 else
-    JAVACMD="java"
+    JAVACMD=java
     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 
 Please set the JAVA_HOME variable in your environment to match the
 Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
 fi
 fi
 
 
 # Increase the maximum file descriptors if we can.
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
 fi
 fi
 
 
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
 
 
 # For Cygwin or MSYS, switch paths to Windows format before running java
 # For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    JAVACMD=`cygpath --unix "$JAVACMD"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
     # Now convert the arguments - kludge to limit ourselves to /bin/sh
     # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
         fi
         fi
-        i=`expr $i + 1`
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
     done
     done
-    case $i in
-        0) set -- ;;
-        1) set -- "$args0" ;;
-        2) set -- "$args0" "$args1" ;;
-        3) set -- "$args0" "$args1" "$args2" ;;
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
 fi
 fi
 
 
-# Escape application args
-save () {
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
-    echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
 
 
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
 
 
 exec "$JAVACMD" "$@"
 exec "$JAVACMD" "$@"

+ 2 - 3
pageview/build.gradle

@@ -25,8 +25,6 @@ android {
     defaultConfig {
     defaultConfig {
         minSdkVersion 22
         minSdkVersion 22
         targetSdkVersion 30
         targetSdkVersion 30
-        versionCode 113
-        versionName "1.2.13"
     }
     }
 
 
     buildTypes {
     buildTypes {
@@ -41,7 +39,8 @@ android {
             buildConfigField("boolean","isDebug","true")
             buildConfigField("boolean","isDebug","true")
         }
         }
     }
     }
-    lintOptions {
+    namespace 'com.ckr.pageview'
+    lint {
         abortOnError false
         abortOnError false
     }
     }
 }
 }

+ 1 - 2
pageview/src/main/AndroidManifest.xml

@@ -1,2 +1 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.ckr.pageview" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" />