liuxiulin 1 vuosi sitten
vanhempi
commit
c3854a14bc
45 muutettua tiedostoa jossa 715 lisäystä ja 243 poistoa
  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 {
         minSdkVersion 14
         targetSdkVersion 30
-        versionCode 240
-        versionName "2.4.0"
     }
     buildTypes {
         release {
@@ -16,6 +14,7 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    namespace 'com.clj.fastble'
 }
 
 dependencies {

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

@@ -1,6 +1,5 @@
 <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以下配置的三个蓝牙权限 -->

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

@@ -560,12 +560,13 @@ public class BleManager {
      * @param data
      * @param callback
      */
-    public void write(BleDevice bleDevice,
+    public void writeData(BleDevice bleDevice,
                       String uuid_service,
                       String uuid_write,
                       byte[] data,
+                      boolean split,
                       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 {
         minSdkVersion 16
         targetSdkVersion 28
-        versionCode 7
-        versionName "3.0.0"
 
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
 
@@ -20,6 +18,7 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    namespace 'com.jaygoo.widget'
 }
 
 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"
                  android:label="@string/app_name"

+ 7 - 4
app/build.gradle

@@ -13,8 +13,8 @@ android {
         applicationId "com.rdiot.yx485"
         minSdk 23
         targetSdk 32
-        versionCode 4
-        versionName "1.0.4"
+        versionCode 6
+        versionName "1.1.0"
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -28,6 +28,8 @@ android {
         }
     }
 
+    namespace 'com.rdiot.yx485'
+
     buildTypes {
         debug {
             signingConfig signingConfigs.release
@@ -62,7 +64,7 @@ android {
 dependencies {
 
     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 'androidx.constraintlayout:constraintlayout:2.1.4'
     testImplementation 'junit:junit:4.13.2'
@@ -108,6 +110,7 @@ dependencies {
     // BRV https://github.com/liangjingkanji/BRV
     implementation 'com.github.liangjingkanji:BRV:1.3.88'
     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'
@@ -144,7 +147,7 @@ dependencies {
     implementation 'com.github.gzu-liyujiang.AndroidPicker:WheelPicker:4.1.11'
 
     implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
-
+    implementation  'com.github.lzyzsd:jsbridge:1.0.5'
 }
 
 // Allow references to generated code

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

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="com.rdiot.yx485">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <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.models
 import com.drake.brv.utils.setup
+import com.king.zxing.util.LogUtils
 import com.rdiot.yx485.R
 import com.rdiot.yx485.bean.DeviceTypeData
 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>())
 
+    var firstBle: Int = 1
+
+    var firstCamera: Boolean by serialLazy(true)
+
     /** 登出 */
     fun logout() {
         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
     /** 记录id */
     @SerialName("record_id")
-    var recordId: String = "", // 1t7svi039tdcoqd4p4g2ncy100mvdglz
+    var recordId: String = "", //
     /** 当前设置的温度 */
     @SerialName("set_temp")
     var setTemp: Int = 25, // 0
@@ -73,6 +73,10 @@ data class RoomData(
     var userId: String = "",
     @SerialName("code")
     var code: String = "",
+    @SerialName("device_type_name")
+    var deviceTypeName: String = "",
+    @SerialName("is_infrared")
+    var isInfrared: Boolean = false,
     @SerialName("icon")
     var icon: String = "",
 
@@ -80,7 +84,10 @@ data class RoomData(
     var isEditMode: Boolean = false,
 
     /** 这个值是房间的,但是每个设备都需要判断,然后展示是否在线,所以在这加一个字段 */
-    var isOnline: Boolean = false
+    @SerialName("is_online")
+    var isOnline: Boolean = false,
+    @SerialName("online_str")
+    var onlineStr: String = "已离线",
 ) : java.io.Serializable {
 
     companion object {

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

@@ -18,7 +18,7 @@ import java.io.File
  */
 object 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/"
 
     /** 头像地址 */
@@ -147,6 +147,8 @@ object Api {
 
         /** 分控风速 */
         const val SUB_WIND_SPEED = "${BASE}/fan_value"
+
+        const val DEVICE_ONLINE = "${BASE}/online/%s"
     }
 
     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) {
     val idArray = arrayListOf<String>()
@@ -378,6 +391,7 @@ fun CoroutineScope.getRoomInfo(roomId: String) =
 fun CoroutineScope.setPower(gateway: String, roomData: RoomData) =
     Post<BaseResp<String>>(if (RoomUtils.isMaster(roomData.code)) Api.Ctrl.POWER else Api.Ctrl.SUB_POWER) {
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "power" to roomData.power
@@ -417,12 +431,17 @@ enum class DeviceCode(val typeCode: String) {
 
     /** 氟系统-分控 */
     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) =
     Post<BaseResp<String>>(Api.Ctrl.MODE) {
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "mode" to modeType.typeCode
@@ -432,6 +451,7 @@ fun CoroutineScope.setMode(gateway: String, roomData: RoomData, modeType: ModeTy
 fun CoroutineScope.setFreshAir(gateway: String, roomData: RoomData) =
     Post<BaseResp<String>>(Api.Ctrl.FRESH_AIR) {
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "power" to roomData.freshAir
@@ -455,6 +475,7 @@ fun CoroutineScope.setTimer(
     time: Float? = null
 ) = Post<BaseResp<String>>(Api.Ctrl.TIMER) {
     json(
+        "record_id" to roomData.recordId,
         "gateway" to gateway,
         "control_number" to roomData.controlNumber,
         "timer_status" to enable,
@@ -466,6 +487,7 @@ fun CoroutineScope.setTimer(
 fun CoroutineScope.setTemp(gateway: String, roomData: RoomData, temp: Int) =
     Post<BaseResp<String>>(Api.Ctrl.TEMP) {
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "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) {
         json(
+            "record_id" to roomData.recordId,
             "gateway" to gateway,
             "control_number" to roomData.controlNumber,
             "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() =
     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()
         if (!isAskingPermission.getAndSet(true)) {
             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() {
         if (dialog?.isVisible != true) {
             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.LocalData
 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.updateFamilyData
 import com.rdiot.yx485.ui.bind.ble.BleCtrl
@@ -51,6 +53,10 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
         bindViewModel.startCountDown()
         startRingAnimation()
 
+        if (bindViewModel.isBindInfrared){
+            bindViewModel.startCheckOnline()
+        }
+
         if (bindViewModel.isBindSub) {
             startBindSubDevice()
         } else {
@@ -67,6 +73,11 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
                 BleCtrl.cancelBind()
             }
         }
+        bindViewModel.deiceStatus.observe(this){
+            if (it == 1) {
+                bindSuccess()
+            }
+        }
     }
 
     /** 开始绑定分控 */
@@ -98,19 +109,35 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
             LogUtils.i(evenString)
             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()) {
                 showErrEventDialog(e.getCode())
                 BleCtrl.cancelBind()
                 bindViewModel.stopCountDown()
+                bindViewModel.stopCheckOnline()
             } else {
                 binding.tvSearch.setText(R.string.device_adding)
             }
 
-            //添加成功
-            if (e.getStep() == 5 && e.getCode() == 0) {
-                bindSuccess()
-            }
         }
 
         lifecycle.addObserver(BleCtrl)
@@ -118,7 +145,7 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
 
     private fun bindSuccess() {
         bindViewModel.stopCountDown()
-
+        bindViewModel.stopCheckOnline()
         if (!bindViewModel.isBindSub) {
             //更新定位信息
             val familyData = LocalData.familyData.value!!
@@ -134,6 +161,18 @@ class BindDeviceFragment : BaseFragment<FraBindDeviceBinding>() {
             binding.tvDebug.text = getString(R.string.add_finish)
             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() }
     }
 

+ 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.ViewGroup
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.databinding.ObservableField
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.findNavController
 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.databinding.FraEnterRoomInfoBinding
 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.goToAppSetting
 import com.rdiot.yx485.util.setClickLimitListener
@@ -56,6 +58,9 @@ class EnterRoomInfoFragment : BaseFragment<FraEnterRoomInfoBinding>() {
     ) {
         binding.vm = bindViewModel
         binding.familyData = LocalData.familyData.value
+        if (bindViewModel.isBindInfrared) {
+            bindViewModel.panelNum=  ObservableField("ESP_GATTS_DEMO")
+        }
         binding.btnNext.setClickLimitListener {
             if (bindViewModel.roomName.get().isNullOrBlank()
                 || bindViewModel.panelNum.get().isNullOrBlank()
@@ -73,7 +78,11 @@ class EnterRoomInfoFragment : BaseFragment<FraEnterRoomInfoBinding>() {
         }
 
         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?) {
         if (dialog?.isVisible != true) {
             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.view.LayoutInflater
-import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.findNavController
@@ -45,13 +44,14 @@ class SelectDeviceTypeFragment : BaseFragment<FraSelectDeviceTypeBinding>() {
         list = ArrayList()
         pagerAdapter = SelectDeviceTypeAdapter(context, list, object : OnItemClickListener {
             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.isBindSub = !RoomUtils.isMaster(deviceTypeData?.code)
-
+                bindViewModel.isBindInfrared = RoomUtils.isInfrared(deviceTypeData?.code)
+                bindViewModel.code = deviceTypeData?.code.toString()
                 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")
     var c: Int = 0, // 0
     @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 com.clj.fastble.BleManager
 import com.clj.fastble.callback.BleGattCallback
+import com.clj.fastble.callback.BleMtuChangedCallback
 import com.clj.fastble.callback.BleNotifyCallback
 import com.clj.fastble.callback.BleScanCallback
 import com.clj.fastble.callback.BleWriteCallback
@@ -64,7 +65,7 @@ object BleCtrl : DefaultLifecycleObserver {
     private var bleDevice: BleDevice? = null
     private var isConnecting = AtomicBoolean(false)
     private var bindJob: Job? = null
-
+    private var split:Boolean = true
 
     /** 初始化BLE */
     private fun initBleManager() {
@@ -106,9 +107,13 @@ object BleCtrl : DefaultLifecycleObserver {
                     }
 
                     override fun onScanning(bleDevice: BleDevice) {
+                        LogUtils.d("deviceName:${bleDevice.name}")
                         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)
                                 startConnect(owner, bleDevice)
                                 BleManager.getInstance().cancelScan()
@@ -171,14 +176,12 @@ object BleCtrl : DefaultLifecycleObserver {
                             isConnecting.set(true)
                             LogUtils.i("开始连接 ${device.name} ${device.mac}")
                         }
-
                         override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                             isConnecting.set(false)
                             LogUtils.e("连接失败(${exception.description}) ${device.name} ${device.mac}")
                             BleManager.getInstance().disconnect(bleDevice)
                             if (cont.isActive) cont.resume(false)
                         }
-
                         override fun onConnectSuccess(
                             device: BleDevice,
                             gatt: BluetoothGatt?,
@@ -187,6 +190,15 @@ object BleCtrl : DefaultLifecycleObserver {
                             isConnecting.set(true)
                             bleDevice = device
                             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(
@@ -231,6 +243,7 @@ object BleCtrl : DefaultLifecycleObserver {
                                 data.byteArrayToHexStr().replace("00", "").hexToByteArray()
                             val dataString = realData.toString(Charset.defaultCharset())
                             val bindBean = Json.decodeFromString<BindBean>(dataString)
+                            LogUtils.d("收到数据 $dataString")
                             if (bindBean.c != 0) {
                                 cancelBind()
                                 LogUtils.e("设备绑定失败 ${bindBean.s} - ${bindBean.c}")
@@ -239,7 +252,7 @@ object BleCtrl : DefaultLifecycleObserver {
                                     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) {
         if (BleManager.getInstance().allConnectedDevice.size >= 1) {
+            if (bleDevice?.name == "ESP_GATTS_DEMO"){
+                split = false
+            }
             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) {
                         Log.i(
                             TAG,
@@ -310,7 +326,7 @@ object BleCtrl : DefaultLifecycleObserver {
  * first Step
  * second Code
  * */
-class BLECtrlEvent(private val e: Pair<Int, Int>) {
+class BLECtrlEvent(private val e: Triple<Int, Int,String>) {
     companion object {
         val CONNECTING = 1 to 0
         val CONNECT_FAILED = 1 to -1001
@@ -324,6 +340,8 @@ class BLECtrlEvent(private val e: Pair<Int, Int>) {
     fun getCode() = e.second
 
 
+    /** 获取Id */
+    fun getId() = e.third
     /** 是否成功 */
     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.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.scopeNetLife
 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.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.delay
 import kotlinx.coroutines.isActive
@@ -31,6 +39,9 @@ class BindViewModel : ViewModel() {
     /** 绑定分控标志 */
     var isBindSub: Boolean = false
 
+    /** 绑定分控标志 */
+    var isBindInfrared: Boolean = false
+
     /** 定位名字(市) */
     var locationName: String? = null
 
@@ -42,9 +53,13 @@ class BindViewModel : ViewModel() {
 
     /** 配网倒计时 */
     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 {
+        lateinit var DeviceStatus: DeviceStatus
         const val COUNT_TIME = 120
     }
 
@@ -81,4 +96,25 @@ class BindViewModel : ViewModel() {
         countDownJob?.cancel()
         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.databinding.ActRoomCtrlMainBinding
 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.clearGoTo
 
 /**
  * 房间控制 容器
@@ -27,6 +29,7 @@ class RoomCtrlMainActivity : BaseActivity<ActRoomCtrlMainBinding>() {
     }
 
     override fun initView(savedInstanceState: Bundle?) {
+
         immersionBar {
             statusBarView(binding.v)
             statusBarColor(R.color.transparent)
@@ -41,6 +44,28 @@ class RoomCtrlMainActivity : BaseActivity<ActRoomCtrlMainBinding>() {
         roomCtrlViewModel.roomData.postValue(tempRoomData as RoomData)
         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)) {
             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) {
         act.clearGoTo(
-            WebActivity.getNewWebIntent(
+          WebActivity.getNewWebIntent(
                 act,
                 "https://app.yongxulvjian.com/#/DataChart/${viewModel.roomData.value?.recordId}/${type}",
                 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 androidx.annotation.IdRes
 import com.drake.brv.annotaion.DividerOrientation
-import com.drake.brv.utils.bindingAdapter
 import com.drake.brv.utils.divider
 import com.drake.brv.utils.grid
 import com.drake.brv.utils.models
@@ -64,10 +63,11 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
             weatherData.now?.let {now->
                 // 温度
                 val ss = SpannableString("${now.temp}℃")
+                val length = ss.length
                 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()
-                ss.setSpan(superScriptTextSpan, 2, 3, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
+                ss.setSpan(superScriptTextSpan, length-1, length, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE)
                 binding.tvTemp.text = ss
             }
             binding.weatherData = weatherData
@@ -113,7 +113,7 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
         scenesAdapter = ScenesAdapter(act, 20.dp, OnSceneItemClickListener)
         binding.pageView.setAdapter(scenesAdapter)
         scenesAdapter.updateAll(list.toList())
-
+    
     }
 
 
@@ -168,9 +168,9 @@ class HomeFragment : BaseFragment<FraHomeBinding>() {
                 } else {
                     tempList.clear()
                     tempList.addAll(roomData)
-                    tempList.forEach {
-                        it.isOnline = familyData.isOnline
-                    }
+//                    tempList.forEach {
+//                        it.isOnline = familyData.isOnline
+//                    }
                     binding.rv.models = tempList
 
                     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.databinding.FraLoginPwdBinding
 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.LoginType
 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.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 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.getWelcome
 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.login.LoginActivity
 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(
                         WebActivity.getNewWebIntent(
                             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.Intent
+import android.graphics.Color
 import android.net.Uri
 import android.os.Bundle
+import android.view.Gravity
 import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
 import android.view.inputmethod.InputMethodManager
 import androidx.appcompat.widget.AppCompatEditText
+import androidx.core.content.ContextCompat
 import androidx.core.widget.doAfterTextChanged
 import androidx.lifecycle.lifecycleScope
 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.ImagePicker
 import com.github.gzuliyujiang.imagepicker.PickCallback
+import com.google.android.material.snackbar.Snackbar
 import com.gyf.immersionbar.ktx.immersionBar
+import com.gyf.immersionbar.ktx.setFitsSystemWindows
 import com.king.zxing.util.LogUtils
 import com.lindroy.iosdialog.IAlertDialog
 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() {
         askPermission(false) { allGranted: Boolean?, _: MutableList<String>? ->
             if (allGranted == true) {
@@ -179,7 +205,11 @@ class UserInfoActivity : BaseActivity<ActUserInfoBinding>() {
             .addItems(listOf(getString(R.string.take_photo), getString(R.string.photo_album)))
             .setOnItemClickListener { position, _, _, _ ->
                 if (position == 0) {
-                    askPermission()
+                    if (LocalData.firstCamera){
+                        showHint()
+                    }else {
+                        askPermission()
+                    }
                 } else if (position == 1) {
                     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.ctrl.RoomCtrlMainActivity
 import com.rdiot.yx485.ui.main.MainActivity
+import com.rdiot.yx485.util.RoomUtils
 import com.rdiot.yx485.util.clearGoTo
 import com.rdiot.yx485.util.curTime
 import com.rdiot.yx485.util.getClearTopIntent
@@ -139,7 +140,10 @@ class RoomFragment : BaseFragment<FraRoomBinding>() {
 
                     resourceRoomDataList.forEach {
                         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

+ 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.Intent
+import android.graphics.Bitmap
 import android.os.Bundle
+import android.util.Log
 import android.view.KeyEvent
 import android.view.View
 import android.webkit.*
 import android.webkit.WebSettings.LOAD_NO_CACHE
 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.king.zxing.util.LogUtils
 import com.rdiot.yx485.BuildConfig
 import com.rdiot.yx485.R
 import com.rdiot.yx485.base.BaseActivity
+import com.rdiot.yx485.base.LocalData
 import com.rdiot.yx485.databinding.ActWebBinding
 
 /**
@@ -49,62 +55,88 @@ class WebActivity : BaseActivity<ActWebBinding>() {
         }
 
         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()
     }
 
     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?) {
                     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
 
             settings.apply {
@@ -122,9 +154,9 @@ class WebActivity : BaseActivity<ActWebBinding>() {
                 domStorageEnabled = true
                 cacheMode = LOAD_NO_CACHE
             }
-            if (BuildConfig.DEBUG) {
-                WebView.setWebContentsDebuggingEnabled(true)
-            }
+
+            WebView.setWebContentsDebuggingEnabled(true)
+
         }
         binding.wv.loadUrl(webUrl)
     }
@@ -171,4 +203,46 @@ class WebActivity : BaseActivity<ActWebBinding>() {
         binding.wv.loadUrl("about:blank")
         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_INTELLIGENCE.typeCode -> R.mipmap.water_intelligence
             DeviceCode.FLUORINE_MASTER.typeCode -> R.mipmap.fluorine_master
+            DeviceCode.INFRARED.typeCode -> R.mipmap.fluorine_master
             else -> R.mipmap.fluorine_sub
         }
 
@@ -64,6 +65,7 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.string.sub_en
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.string.intelligence_en
             DeviceCode.FLUORINE_MASTER.typeCode -> R.string.master_en
+            DeviceCode.INFRARED.typeCode -> R.string.infrared
             else -> R.string.sub_en
         }
         return result
@@ -76,6 +78,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_4AB0E4
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_60A736
             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
         }
         val shape = GradientDrawable()
@@ -91,6 +95,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_387084
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_517232
             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
         }
         return ContextCompat.getColor(context, result)
@@ -103,6 +109,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> R.color.c_55DCDF
             DeviceCode.WATER_INTELLIGENCE.typeCode -> R.color.c_B1EB5C
             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
         }
         val shape = GradientDrawable()
@@ -125,6 +133,38 @@ object RoomUtils {
         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
     fun isMaster(code: String?): Boolean {
@@ -133,6 +173,8 @@ object RoomUtils {
             DeviceCode.WATER_SUB.typeCode -> false
             DeviceCode.WATER_INTELLIGENCE.typeCode -> false
             DeviceCode.FLUORINE_MASTER.typeCode -> true
+            DeviceCode.INFRARED.typeCode -> true
+            DeviceCode.DRAUGHT_DISTRIBUTING_BOX.typeCode -> true
             else -> false
         }
         return result

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

@@ -17,17 +17,16 @@
             android:layout_width="match_parent"
             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:background="@color/white"
             android:layout_width="match_parent"
-            android:visibility="gone"
             android:layout_height="match_parent" />
 
 

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

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

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

@@ -208,9 +208,10 @@
                             <androidx.appcompat.widget.AppCompatImageView
                                 srcString="@{String.format(`icon_weather_%s`,weatherData.now.iconCode)}"
                                 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_constraintEnd_toEndOf="parent"
                                 app:layout_constraintTop_toTopOf="parent"

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

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

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

@@ -19,7 +19,7 @@
         <androidx.core.widget.NestedScrollView
             android:layout_width="match_parent"
             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_constraintTop_toTopOf="parent">
 
@@ -101,6 +101,49 @@
 
         </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
             android:id="@+id/btn_next"

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

@@ -74,7 +74,7 @@
             <TextView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@{vm.name}"
+                android:text="@{vm.deviceTypeName}"
                 android:textColor="@color/c_999999"
                 android:textSize="12sp"
                 tools:text="主控制器"
@@ -94,22 +94,22 @@
                 tools:text="湿度:55%"
                 android:textSize="12sp"
                 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"
                 android:textColor="@color/c_14C9C8"
                 android:layout_marginStart="8dp"/>
 
             <TextView
                 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"
-                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>
 
@@ -120,7 +120,7 @@
             android:layout_width="48dp"
             android:layout_height="24dp"
             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_constraintEnd_toEndOf="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="no_room">您还没有添加房间</string>
+    <string name="no_device">您还没有添加设备</string>
+    <string name="add_device">添加设备</string>
     <string name="add_room">添加房间</string>
     <string name="select_device_type">选择设备类型</string>
     <string name="add">添加</string>
@@ -23,7 +25,8 @@
     <string name="no_net_refresh">立即刷新</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="fluorine_system">氟系统</string>
@@ -108,6 +111,7 @@
 
     <string name="reset_tips_1">1、开机状态下,长按『菜单键』3s进入设置界面</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_2">2、使用APP扫码绑定</string>
     <string name="next_step">下一步</string>

+ 4 - 5
arcseekbar/build.gradle

@@ -4,13 +4,11 @@ apply plugin: 'com.android.library'
 android {
 
     compileSdkVersion 29
-    buildToolsVersion "29.0.3"
+    buildToolsVersion "30.0.3"
 
     defaultConfig {
         minSdkVersion 16
         targetSdkVersion 29
-        versionCode 3
-        versionName "1.0.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
 
@@ -20,12 +18,13 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
-
-    lintOptions {
+    namespace 'com.king.view.arcseekbar'
+    lint {
         abortOnError false
         warning 'InvalidPackage'
     }
 
+
 }
 
 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.
 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 'com.google.dagger.hilt.android' version '2.44' 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
 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
 zipStorePath=wrapper/dists
 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");
 # 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
+
 # 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
-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_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.
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
 
 warn () {
     echo "$*"
-}
+} >&2
 
 die () {
     echo
     echo "$*"
     echo
     exit 1
-}
+} >&2
 
 # OS specific support (must be 'true' or 'false').
 cygwin=false
 msys=false
 darwin=false
 nonstop=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-  NONSTOP* )
-    nonstop=true
-    ;;
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
 esac
 
 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 [ -x "$JAVA_HOME/jre/sh/java" ] ; then
         # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
+        JAVACMD=$JAVA_HOME/jre/sh/java
     else
-        JAVACMD="$JAVA_HOME/bin/java"
+        JAVACMD=$JAVA_HOME/bin/java
     fi
     if [ ! -x "$JAVACMD" ] ; then
         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."
     fi
 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.
 
 Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
+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
 
-# 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
-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
-    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
-        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
-    case $i in
-        0) set -- ;;
-        1) set -- "$args0" ;;
-        2) set -- "$args0" "$args1" ;;
-        3) set -- "$args0" "$args1" "$args2" ;;
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
 fi
 
-# Escape application args
-save () {
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
-    echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+#   * $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" "$@"

+ 2 - 3
pageview/build.gradle

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