浏览代码

1。护士主机,分机增加声网探视功能
2。门口机增加大朝华10寸机的适配

xunchuanzhi 1 年之前
父节点
当前提交
26185246c5
共有 88 个文件被更改,包括 5756 次插入116 次删除
  1. 2 4
      android_bed/build.gradle
  2. 5 0
      android_bed/src/main/AndroidManifest.xml
  3. 1 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivationActivity.kt
  4. 50 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivity.kt
  5. 339 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/VisitListActivity.kt
  6. 112 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/adapter/VisitListAdapter.kt
  7. 2 2
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/di/CallingbedComponent.kt
  8. 5 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/NursingWorkFragment.kt
  9. 396 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/VisitCallFragment.kt
  10. 1 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/hardware/imp/WdchHardTools.java
  11. 11 1
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/settings/SettingConfig.java
  12. 9 0
      android_bed/src/main/res/drawable/shape_visit_bg.xml
  13. 101 0
      android_bed/src/main/res/layout/adapter_visit_frame_part.xml
  14. 145 0
      android_bed/src/main/res/layout/fragment_visit_agore_call.xml
  15. 184 0
      android_bed/src/main/res/layout/visit_list_lay.xml
  16. 5 7
      android_host/build.gradle
  17. 7 0
      android_host/src/main/AndroidManifest.xml
  18. 182 24
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt
  19. 96 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/VisitGroupAdapter.kt
  20. 149 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/VisitListAdapter.kt
  21. 7 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/di/NurseHomeComponent.kt
  22. 2 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/FramePartFragment.kt
  23. 315 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitCallFragment.kt
  24. 389 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitMainFragment.kt
  25. 371 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitSetFragment.kt
  26. 156 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/util/BluetoothUtil.java
  27. 143 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/util/NotificationUtil.java
  28. 146 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitService.java
  29. 80 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitSocketConnect.java
  30. 384 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitSocketConnectLine.java
  31. 194 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/tcp/VisitTcpClient.java
  32. 249 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/tcp/VisitTcpClientHandler.java
  33. 44 20
      android_host/src/main/res/layout/activity_new_nurse_home.xml
  34. 246 0
      android_host/src/main/res/layout/adapter_visit_frame_part.xml
  35. 106 0
      android_host/src/main/res/layout/fragment_visit_agore_call.xml
  36. 153 0
      android_host/src/main/res/layout/fragment_visit_agore_main.xml
  37. 281 0
      android_host/src/main/res/layout/fragment_visit_through.xml
  38. 1 0
      android_host/src/main/res/layout/sky_voice_call_layout.xml
  39. 63 0
      android_host/src/main/res/layout/visit_set_item_lay.xml
  40. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_all_img.png
  41. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_all_img_w.png
  42. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_call_no.png
  43. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_call_yes.png
  44. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_exit.png
  45. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_list_img.png
  46. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_list_img_w.png
  47. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_reject.png
  48. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_sk_img.png
  49. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_sk_img_w.png
  50. 二进制
      android_host/src/main/res/mipmap-xhdpi/visit_yes.png
  51. 1 0
      android_host/src/main/res/values-es/strings.xml
  52. 1 0
      android_host/src/main/res/values-ru/strings.xml
  53. 1 0
      android_host/src/main/res/values-zh/strings.xml
  54. 1 0
      android_host/src/main/res/values/strings.xml
  55. 2 7
      android_mobile/build.gradle
  56. 1 7
      android_visiting/build.gradle
  57. 2 0
      app/build.gradle
  58. 56 0
      bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java
  59. 1 6
      callingdoor/build.gradle
  60. 1 1
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/activity/CallingdoorActivationActivity.kt
  61. 3 1
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/YhFragment.kt
  62. 2 1
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/W3288HardTools.java
  63. 23 13
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/WDCHHardTools.java
  64. 2 1
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Wa133HardTools.java
  65. 0 1
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Z3128HardTools.java
  66. 2 0
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Z3368HardTools.java
  67. 7 3
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/DoorLightHelper.java
  68. 6 5
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/SOSHelper.java
  69. 1 0
      middleware/build.gradle
  70. 35 8
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constant.java
  71. 18 2
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/MessageEvent.java
  72. 12 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/PartSettingDO.java
  73. 233 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/VisitPageVo.java
  74. 15 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java
  75. 18 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceUtil.java
  76. 128 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/SwVisitUtil.java
  77. 7 1
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpModel.java
  78. 33 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpAction.java
  79. 3 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpType.java
  80. 0 0
      resource/src/main/res/drawable/shape_bt_call_bg.xml
  81. 8 0
      resource/src/main/res/drawable/shape_bt_main_add_bg.xml
  82. 0 0
      resource/src/main/res/drawable/shape_bt_main_hh_bg.xml
  83. 0 0
      resource/src/main/res/drawable/shape_bt_main_list_bg.xml
  84. 0 0
      resource/src/main/res/drawable/shape_other_item_bg.xml
  85. 0 0
      resource/src/main/res/mipmap-xhdpi/jian_bai.png
  86. 0 0
      resource/src/main/res/mipmap-xhdpi/jr.png
  87. 0 0
      resource/src/main/res/mipmap-xhdpi/shan.png
  88. 1 0
      welcome/build.gradle

+ 2 - 4
android_bed/build.gradle

@@ -30,11 +30,7 @@ android {
         buildConfigField "String", "BUILD_TIME", getDate()
         buildConfigField 'String', 'VERSION_NAME', "\"${project.rootProject.ext.app_version}\""
         buildConfigField 'String', 'VERSION_CODE', "\"${project.rootProject.ext.app_version_code}\""
-        buildConfigField 'String', 'iscallingdoor', "\"${project.rootProject.ext.callingdoor}\""
         buildConfigField 'String', 'isandroid_bed', "\"${project.rootProject.ext.android_bed}\""
-        buildConfigField 'String', 'isandroid_host', "\"${project.rootProject.ext.android_host}\""
-        buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
-        buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
         buildConfigField 'String', 'open_sleep', "\"${project.rootProject.ext.open_sleep}\""
         buildConfigField 'String', 'open_433', "\"${project.rootProject.ext.open_433}\""
@@ -203,6 +199,8 @@ dependencies {
 
     compile 'com.inuker.bluetooth:library:1.4.0'
 
+    implementation 'io.agora.rtc:full-sdk:4.2.6'
+
 }
 
 /**

+ 5 - 0
android_bed/src/main/AndroidManifest.xml

@@ -154,6 +154,11 @@
             android:screenOrientation="nosensor"
             android:launchMode="singleTask"/>
 
+        <activity android:name="com.wdkl.app.ncs.callingbed.activity.VisitListActivity"
+            android:turnScreenOn="true"
+            android:screenOrientation="nosensor"
+            android:launchMode="singleTask"/>
+
 
         <service android:name="com.wdkl.app.ncs.callingbed.bt_gateway.BluetoothService"/>
 

+ 1 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivationActivity.kt

@@ -386,6 +386,7 @@ class CallingbedActivationActivity  : BaseActivity<CallingbedActivationPresenter
         Constant.DEVICE_ID = deviceInfo.id
         Constant.PART_ID = deviceInfo.partId
         Constant.BED_NAME = deviceInfo.fullName
+        Constant.union_id = deviceInfo.unionId
 
         Constant.DEVICE_CODE = deviceInfo.code
         Constant.DEVICE_MODEL = deviceInfo.model

+ 50 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivity.kt

@@ -1445,6 +1445,7 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
         when (messageEvent.getType()) {
             //退出通话界面
             Constant.EVENT_REMOVE_CALL_FRAGMENT -> {
+
                 Constant.CALL_STATE = Constant.CALL_STANDBY
                 //SerialPortHelper.setCallStatus("0")
                 if (skyCallFragment != null) {
@@ -1758,6 +1759,44 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                             Constant.CALL_STATE = Constant.CALL_STANDBY
                             //SerialPortHelper.setCallStatus("0")
                         }
+                    } else if (tcpModel.type == TcpType.REMOTE_VISIT) {
+                        if (tcpModel.data != null) {
+                            if (tcpModel.action == TcpAction.REMOTEVISITAction.CALL) {
+                                var channelName = tcpModel.data.toString()
+                                //确保探视tcp正常且房间号不为空,呼叫状态为待机中
+                                if (Constant.TCP_CONNECTED && !TextUtils.isEmpty(channelName) && Constant.CALL_STATE==Constant.CALL_STANDBY) {
+                                     if (playing) {
+                                        stopBroadcast(false)
+                                    }
+                                    if (Constant.NursingTitle.equals(getString(R.string.enter_nursing))) {
+                                        return
+                                    } else {
+                                        //这里直接进入
+                                        var fragment = VisitCallFragment()
+                                        var bundle = Bundle()
+                                        bundle.putString("channelName", channelName)
+//                                      bundle.putString("channelName", "2")
+                                        fragment.arguments = bundle
+                                        addCallFragment(fragment)
+                                    }
+                                }else{
+                                    showMessage(R.string.call_init_error)
+                                    Constant.CALL_STATE = Constant.CALL_STANDBY
+                                    //VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
+                                    val rejectTcp = SwVisitUtil.BedvisitIngCall( channelName)
+                                    TcpClient.getInstance().sendMsg(rejectTcp.toJson())
+                                }
+
+                            } else if (tcpModel.action == TcpAction.REMOTEVISITAction.HANDOFF) {
+                                //收到 挂断
+                                EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                            } else if (tcpModel.action == TcpAction.REMOTEVISITAction.CANCEL) {
+                                //收到 取消
+                                EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                            }
+                        }
+
+
                     }
                 }
             }
@@ -1842,6 +1881,17 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                 }
 
             }
+//             //进入院外探视
+//            Constant.EVENT_VISIT_START -> {
+//
+//
+//            }
+//
+//            //退出院外探视
+//            Constant.EVENT_VISIT_STOP -> {
+//
+//
+//            }
 
             //倒计时
             Constant.EVENT_COUNTDOWN_TIME -> {

+ 339 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/VisitListActivity.kt

@@ -0,0 +1,339 @@
+package com.wdkl.app.ncs.callingbed.activity
+
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.View
+import androidx.recyclerview.widget.GridLayoutManager
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.clj.fastble.utils.HexUtil
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.enation.javashop.net.engine.model.NetState
+import com.google.common.reflect.TypeToken
+import com.google.gson.FieldNamingPolicy
+import com.google.gson.GsonBuilder
+import com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS
+import com.inuker.bluetooth.library.connect.listener.BluetoothStateListener
+import com.inuker.bluetooth.library.connect.options.BleConnectOptions
+import com.inuker.bluetooth.library.connect.response.BleNotifyResponse
+import com.inuker.bluetooth.library.connect.response.BleWriteResponse
+import com.inuker.bluetooth.library.receiver.listener.BluetoothBondListener
+import com.inuker.bluetooth.library.search.SearchRequest
+import com.inuker.bluetooth.library.search.SearchResult
+import com.inuker.bluetooth.library.search.response.SearchResponse
+import com.inuker.bluetooth.library.utils.BluetoothLog
+import com.inuker.bluetooth.library.utils.ByteUtils
+import com.wdkl.app.ncs.callingbed.R
+import com.wdkl.app.ncs.callingbed.adapter.DeviceLinAdapter
+import com.wdkl.app.ncs.callingbed.adapter.VisitListAdapter
+import com.wdkl.app.ncs.callingbed.databinding.DeviceLinLayBinding
+import com.wdkl.app.ncs.callingbed.databinding.VisitListLayBinding
+import com.wdkl.app.ncs.callingbed.dialog.DeviceLinkDialogHelper
+import com.wdkl.app.ncs.callingbed.dialog.DeviceLinkSleepDialogHelper
+import com.wdkl.app.ncs.callingbed.dialog.WifiSetDialogHelper
+import com.wdkl.app.ncs.callingbed.launch.CallingbedLaunch
+import com.wdkl.app.ncs.callingbed.settings.SettingConfig
+import com.wdkl.app.ncs.callingbed.sleep.SleepDataParse
+import com.wdkl.app.ncs.callingbed.utils.ClientManager
+import com.wdkl.ncs.android.lib.base.BaseActivity
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed.BedDeviceLinkageActivityContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.BedDeviceLinkagePresenter
+import com.wdkl.ncs.android.middleware.model.vo.DeviceLinVo
+import com.wdkl.ncs.android.middleware.model.vo.VisitPageVo
+import kotlinx.android.synthetic.main.callingbed_setting_main.*
+import kotlinx.android.synthetic.main.device_lin_lay.*
+import kotlinx.android.synthetic.main.device_lin_lay.dv_lin_emptyImageView
+import kotlinx.android.synthetic.main.sleep_activity.*
+import kotlinx.android.synthetic.main.view_title_layout.*
+import kotlinx.android.synthetic.main.visit_list_lay.*
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.*
+import java.util.concurrent.TimeUnit
+import kotlin.collections.ArrayList
+
+
+/**
+ * 探视列表界面
+ * */
+@Router(path = "/callingbed/visitlist")
+class VisitListActivity : BaseActivity<BedDeviceLinkagePresenter, VisitListLayBinding>(), BedDeviceLinkageActivityContract.View {
+
+    private val TAG = "VisitListActivity"
+
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    var visitListAdapter: VisitListAdapter? = null
+
+    private var data = ArrayList<VisitPageVo.DataBean>()
+    private lateinit var responsedata : VisitPageVo
+    var  shopUnionId:String=""
+
+    var pageNo = 1
+    var status:String="1"
+    override fun getLayId(): Int {
+        return R.layout.visit_list_lay
+    }
+
+    override fun bindDagger() {
+        CallingbedLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        showui()
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+
+        visitListAdapter = VisitListAdapter(ArrayList())
+        delegateAdapter.addAdapter(visitListAdapter)
+
+        /**配置到RecycleView*/
+        bed_sw_visit_recycler.layoutManager = virtualLayoutManager
+        bed_sw_visit_recycler.adapter = delegateAdapter
+        checkServer()
+
+
+    }
+
+    override fun bindEvent() {
+        //返回上一层
+        view_title_layout_return.setOnClickListener {
+            view_title_layout_img.visibility = View.GONE
+            view_title_layout_tv_no.visibility =View.GONE
+            finish()
+        }
+        bed_sw_visit_refresh.setOnRefreshListener {
+            if (!Constant.union_id.equals("")) {
+                pageNo = 1
+                checkServer()
+            } else {
+                showMessage("no union id")
+            }
+        }
+
+        bed_sw_visit_refresh.setOnLoadMoreListener {
+            if (!Constant.union_id.equals("")) {
+                pageNo += 1
+                checkServer()
+            } else {
+                showMessage("no union id")
+            }
+        }
+        bed_sw_visit_switch.setOnCheckedChangeListener { buttonView, isChecked ->
+            if (isChecked) {
+                showMessage("探视来电自动接听已开启")
+                SettingConfig.setSwCall(activity, true)
+            } else {
+                showMessage("探视来电自动接听已关闭")
+                SettingConfig.setSwCall(activity, false)
+            }
+        }
+    }
+    private fun checkServer() {
+        Thread {
+            val okHttpClient = OkHttpClient().newBuilder()
+                    .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .build()
+//            val baseUrl: String ="http://192.168.1.199:7000/ncs_visit_log/get_page"
+            shopUnionId = Constant.union_id
+            if (shopUnionId.equals("")) {
+                showMessage("机构id为空")
+                initView(2)
+                return@Thread
+            }
+//            val url = "$baseUrl?sn=$sn&reportTime=$reportTime"
+            val baseUrl: String ="https://api.base.wdklian.com/ncs_visit_log/get_page"
+
+            val requestBody = FormBody.Builder()
+                    .add("pageNo", pageNo.toString())
+                    .add("pageSize", "5")
+                    //寻找 这里写死了
+//                    .add("shopUnionId", "610a4ebae21b840007887b95")
+                    .add("shopUnionId", shopUnionId)
+                    .add("memberId", "")
+                    .add("status", status)
+                    .add("deviceId", Constant.DEVICE_ID.toString())
+                    .build()
+
+            val request = Request.Builder()
+                    .url(baseUrl)
+                    .post(requestBody)
+                    .build()
+
+            try {
+                Log.i(TAG, "start check server: $baseUrl")
+                val response = okHttpClient.newCall(request).execute()
+                if (response != null && response.isSuccessful) {
+                    //接口数据获取成功,检查设备是否已注册
+                    val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                    val itemType = object : TypeToken<VisitPageVo>(){}.type
+                    responsedata= gson.fromJson(response.body()?.string(), itemType)
+                    data = responsedata.data as ArrayList<VisitPageVo.DataBean>
+                    initView(1)
+                } else {
+                    //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                    initView(2)
+                }
+            } catch (e: Exception) {
+                initView(2)
+                //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                Log.e(TAG, "check server exception:")
+                e.printStackTrace()
+            }
+        }.start()
+    }
+
+    private fun initView(type: Int) {
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            // 在这里执行与UI元素相关的操作
+            try {
+                if (type==1){
+                    bed_sw_visit_refresh.finishRefresh()
+                    bed_sw_visit_refresh.finishLoadMore()
+                    if (data!=null && data.size>0){
+                        bed_sw_visit_emptyImageView.visibility= View.GONE
+                        visitListAdapter!!.updateData(data,status,pageNo)
+                        if (pageNo!=1){
+                            visitListAdapter!!.data.addAll(data)
+                        }else{
+                            visitListAdapter!!.data.clear()
+                            visitListAdapter!!.data.addAll(data)
+                        }
+                        //Log.i(TAG,"其它主机数量 " + devices.size)
+                        visitListAdapter!!.notifyDataSetChanged()
+                    }else{
+
+                        if (pageNo>1){
+                            showMessage("没有更多数据")
+                            return@post
+                        }
+                        bed_sw_visit_emptyImageView.visibility= View.VISIBLE
+                    }
+                }else{
+                    bed_sw_visit_refresh.finishRefresh()
+                    bed_sw_visit_refresh.finishLoadMore()
+                    bed_sw_visit_emptyImageView.visibility= View.VISIBLE
+                }
+            } catch (e: Exception) {
+                initView(2)
+            }
+        }
+
+    }
+
+
+    private fun showui(){
+        view_title_layout_tv_hospital_name.setText(R.string.str_back)
+        view_title_layout_img.visibility = View.VISIBLE
+        view_title_layout_tv_no.visibility =View.VISIBLE
+        view_title_layout_tv_no.setText(R.string.device_linkage)
+
+        bed_sw_visit_name.text =Constant.FJ_NAME
+        bed_sw_visit_sex.text =Constant.FJ_SEX
+        bed_sw_visit_age.text =Constant.FJ_AGE
+        bed_sw_visit_switch.isChecked = SettingConfig.getSwCall(activity)
+
+        //网络图标
+        if ( Constant.network_state == 1){
+            view_title_layout_iv_wifi.visibility = View.VISIBLE
+            view_title_layout_iv_ethernet.visibility = View.GONE
+            view_title_layout_iv_wifi.setImageResource(R.mipmap.ic_wifi_success)
+        }else if ( Constant.network_state == 2){
+            view_title_layout_iv_wifi.visibility = View.GONE
+            view_title_layout_iv_ethernet.visibility = View.VISIBLE
+            view_title_layout_iv_ethernet.setImageResource(R.mipmap.ic_ethernet_success)
+        }else{
+            view_title_layout_iv_wifi.visibility = View.GONE
+            view_title_layout_iv_ethernet.visibility = View.VISIBLE
+            view_title_layout_iv_ethernet.setImageResource(R.mipmap.ic_ethernet_fail)
+        }
+        //蓝牙图标
+        if ( Constant.BT_state == 0){
+            view_title_layout_iv_bt.setImageResource(R.mipmap.ic_bt_success)
+        }
+        //白天/黑夜
+        if (Constant.day_state == 0){
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_daylight)
+        }else{
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_night)
+        }
+        view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_success)
+    }
+
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+
+        super.onStop()
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            Constant.EVENT_FINISHh -> {
+                finish()
+            }
+        }
+    }
+
+    override fun setDevicesList(advices: ArrayList<DeviceLinVo>) {
+
+    }
+
+    override fun onNoneNet() {
+        dv_lin_emptyImageView.visibility =View.VISIBLE
+    }
+
+
+    //数据加载错误
+    override fun onError(message: String, type: Int) {
+        dv_lin_emptyImageView.visibility =View.VISIBLE
+    }
+    //数据加载完成
+    override fun complete(message: String, type: Int) {
+
+    }
+
+    //开始获取数据
+    override fun start() {
+        //
+    }
+
+    //网络监听
+    override fun networkMonitor(state: NetState) {
+        state.filter(onMobile = {
+
+        }, onWifi = {
+
+        }, offline = {
+
+        })
+    }
+
+    override fun destory() {
+
+    }
+
+
+}

+ 112 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/adapter/VisitListAdapter.kt

@@ -0,0 +1,112 @@
+package com.wdkl.app.ncs.callingbed.adapter
+
+import android.graphics.Color
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.wdkl.app.ncs.callingbed.R
+import com.wdkl.app.ncs.callingbed.databinding.AdapterVisitFramePartBinding
+
+
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.middleware.model.vo.VisitPageVo
+
+/**
+ * 预约列表适配器
+ */
+class VisitListAdapter(val data: ArrayList<VisitPageVo.DataBean>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterVisitFramePartBinding>, VisitPageVo.DataBean>() {
+
+    private lateinit var onItemClickListener: OnItemClickListener
+
+    fun setOnItemClickListener(listener: OnItemClickListener) {
+        this.onItemClickListener = listener
+    }
+    /**
+     * 数据提供者
+     */
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    /**
+     * Item坐标
+     */
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    /**
+     * 获取Item总数
+     */
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    /**
+     * 创建LayoutHelper
+     */
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        return LinearLayoutHelper(0, data.size)
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterVisitFramePartBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_visit_frame_part)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterVisitFramePartBinding>, position: Int) {
+        holder?.bind { binding ->
+            try {
+                val itemData = getItem(position)
+//                binding.visitName.text = itemData.member_name
+                binding.visitType.text = "关系:"+itemData.relation
+                binding.visitRegionTxt.text =itemData.customer_name +"  "+itemData.bed_no+"床"
+                binding.visitTime.text = "探视时间:"+TimeHandle.getDateTime(itemData.start_time * 1000, "yyyy.MM.dd HH:mm")+"-"+TimeHandle.getDateTime(itemData.end_time * 1000, "yyyy.MM.dd HH:mm")
+
+                binding.visitSkType.visibility=View.GONE
+             if (itemData.status==2 && extraParam.equals("")){
+                    binding.visitSkType.visibility=View.VISIBLE
+                    binding.visitSkType.text="已拒绝"
+                    binding.visitSkType.setTextColor(Color.parseColor("#F65F5F"))
+                }else if (itemData.status==3 && extraParam.equals("")){
+                    binding.visitSkType.visibility=View.VISIBLE
+                    binding.visitSkType.text="已完成"
+                    binding.visitSkType.setTextColor(Color.parseColor("#2F9DF1"))
+                }
+
+            } catch (e: Exception) {
+                Log.e(" BroadcastListAdapter",e.toString())
+            }
+
+
+        }
+    }
+
+
+    interface OnItemClickListener {
+        fun onItemClick(ID:Int,type: String)
+    }
+    private  var extraParam: String=""
+
+    fun updateData(newData: List<VisitPageVo.DataBean>, newExtraParam: String,pageNo:Int) {
+        if(pageNo!=1){
+            data.addAll(newData)
+        }else{
+            data.clear()
+            data.addAll(newData)
+        }
+        extraParam = newExtraParam
+        notifyDataSetChanged()
+    }
+}
+
+

+ 2 - 2
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/di/CallingbedComponent.kt

@@ -44,7 +44,7 @@ interface CallingbedComponent {
 
     fun inject(activity: DeviceBluetoothLinkageActivity)
 
-
+    fun inject(activity: VisitListActivity)
 
     fun inject(fragment: MainFragment)
 
@@ -81,7 +81,7 @@ interface CallingbedComponent {
 
     fun inject(fragment: SleepDataTableFragment)
 
-
+    fun inject(fragment: VisitCallFragment)
 
 
 }

+ 5 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/NursingWorkFragment.kt

@@ -223,6 +223,11 @@ class NursingWorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, Nursin
 //                    intent.setClass(activity, DepartmentInfoActivity::class.java)
 //                    activity.startActivity(intent)
                     showMessage(R.string.str_not_support)
+                }else if (allOrders.get(keyId).act_name.equals("visitListActivity")){
+//                    探视列表
+                    val intent = Intent()
+                    intent.setClass(activity, VisitListActivity::class.java)
+                    activity.startActivity(intent)
                 }
 
             } else if (allOrders.get(keyId).type.equals("SYS")){

+ 396 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/VisitCallFragment.kt

@@ -0,0 +1,396 @@
+package com.wdkl.app.ncs.callingbed.fragment
+
+import android.graphics.Color
+import android.os.CountDownTimer
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.SurfaceView
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed.BuildConfig
+import com.wdkl.app.ncs.callingbed.R
+import com.wdkl.app.ncs.callingbed.databinding.FragmentVisitAgoreCallBinding
+import com.wdkl.app.ncs.callingbed.launch.CallingbedLaunch
+import com.wdkl.app.ncs.callingbed.settings.SettingConfig
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.BroadcastContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.BroadcastPresenter
+import com.wdkl.ncs.android.middleware.model.dos.BroadcastDO
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.SwVisitUtil
+import com.wdkl.ncs.android.middleware.utils.RingPlayHelper
+import io.agora.rtc2.*
+import io.agora.rtc2.video.VideoCanvas
+import io.agora.rtc2.video.VideoEncoderConfiguration
+import kotlinx.android.synthetic.main.fragment_visit_agore_call.*
+import kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import kotlinx.android.synthetic.main.view_title_layout.*
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.concurrent.TimeUnit
+
+
+class VisitCallFragment : BaseFragment<BroadcastPresenter, FragmentVisitAgoreCallBinding>(), BroadcastContract.View {
+
+    val appId: String = "e1d55db9bcd646f2a23694494526b9d0"
+
+    var TAG = VisitCallFragment::class.java.getSimpleName()
+
+
+    // 当前用户uid
+    var uuid:Int=0
+    // 频道Token
+    var token:String=""
+    //
+    var channelName:String=""
+    var localAudioMute = false
+    var speakerphone = false
+    var is_bed_user = false
+
+
+    /**
+     * 提供layoutID
+     */
+    override fun getLayId(): Int {
+        return R.layout.fragment_visit_agore_call
+    }
+
+    /**
+     *初始化依赖注入
+     */
+    override fun bindDagger() {
+        CallingbedLaunch.component.inject(this)
+    }
+
+    /**
+     *初始化操作
+     */
+    override fun init() {
+        channelName = requireArguments().getString("channelName").toString()
+        Constant.CALL_STATE = Constant.CALL_VISIT_CALLING
+//            uuid =((Math.random()*9+1)*100000).toInt().toString()
+        uuid =channelName.toInt()+30
+        if(SettingConfig.getSwCall(activity)){
+            //自动接听
+            visit_call_bt.visibility =View.VISIBLE
+            visit_call_incoming.visibility =View.GONE
+            visit_voice_call_timer.visibility = View.VISIBLE
+            visit_voice_call_timer.base = SystemClock.elapsedRealtime()
+            visit_voice_call_timer.start()
+            if (channelName == ""){
+                showMessage("房间号为空")
+            }else{
+                checkServer()
+            }
+
+        }else{
+            //响铃
+            RingPlayHelper.playRingTone(activity, R.raw.ring_tone, true, 0)
+            visit_call_bt.visibility =View.GONE
+            visit_call_incoming.visibility =View.VISIBLE
+            //初始化计时器
+            initCountDownTimer(visit_voice_call_timer2)
+            startTimer()
+        }
+
+    }
+
+
+    /**
+     *绑定事件
+     */
+    override fun bindEvent() {
+        //静音
+        visit_voice_call_mute.setOnClickListener {
+            localAudioMute = !localAudioMute
+//            if (localAudioMute){
+//                mRtcEngine?.enableAudio()
+//            }else{
+//                mRtcEngine?.disableAudio()
+//            }
+            mRtcEngine?.muteLocalAudioStream(localAudioMute)
+            visit_voice_call_mute.isSelected = localAudioMute
+        }
+        //喇叭
+        visit_call_speaker.setOnClickListener {
+            speakerphone = !speakerphone
+//            mRtcEngine?.setDefaultAudioRoutetoSpeakerphone(speakerphone)
+            mRtcEngine?.muteAllRemoteAudioStreams(speakerphone)
+            visit_call_speaker.isSelected = speakerphone
+        }
+
+        //来电接听
+        visit_call_ring_pickup_audio.setOnClickListener {
+            visit_call_bt.visibility =View.VISIBLE
+            visit_call_incoming.visibility =View.GONE
+            RingPlayHelper.stopRingTone()
+            cancelTimer()
+            visit_voice_call_timer.visibility = View.VISIBLE
+            visit_voice_call_timer.base = SystemClock.elapsedRealtime()
+            visit_voice_call_timer.start()
+            Constant.CALL_STATE = Constant.CALL_VISIT_CALLING
+            if (channelName == ""){
+                showMessage("房间号为空")
+            }else{
+                checkServer()
+            }
+        }
+
+        //来电拒绝
+        visit_call_ring_reject.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            val rejectTcp = SwVisitUtil.BedvisitRemote(channelName)
+            TcpClient.getInstance().sendMsg(rejectTcp.toJson())
+            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+        }
+
+
+        //挂断
+        visit_voice_call_hangup.setOnClickListener {
+         //发送tcp给探视服务器,关闭房间并退出,弹窗提示
+            if (visit_voice_call_timer != null) {
+                visit_voice_call_timer.stop()
+            }
+            val rejectTcp = SwVisitUtil.BedvisitHangup(Constant.DEVICE_ID, channelName)
+            TcpClient.getInstance().sendMsg(rejectTcp.toJson())
+            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+        }
+    }
+
+    /**
+     *页面销毁回调
+     */
+    override fun destory() {
+    }
+    private fun checkServer() {
+        Thread {
+            val okHttpClient = OkHttpClient().newBuilder()
+                    .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .build()
+//            val baseUrl: String ="http://192.168.1.199:7000/ncs_visit_log/get_token"
+            val baseUrl: String ="https://api.base.wdklian.com/ncs_visit_log/get_token"
+            val requestBody = FormBody.Builder()
+                    .add("roomName", channelName)
+                    .add("uid", uuid.toString())
+                    .build()
+
+            val request = Request.Builder()
+                    .url(baseUrl)
+                    .post(requestBody)
+                    .build()
+
+            try {
+                Log.i(TAG, "start check server: $baseUrl")
+                val response = okHttpClient.newCall(request).execute()
+                if (response != null && response.isSuccessful) {
+                    //接口数据获取成功,检查设备是否已注册
+                    val responseBody = response.body()
+                    if (responseBody != null) {
+                        token = responseBody.string()
+                        initView(1, 0)
+                    }
+                }
+            } catch (e: Exception) {
+                //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                Log.e(TAG, "check server exception:")
+                e.printStackTrace()
+            }
+        }.start()
+    }
+
+
+    private var mRtcEngine: RtcEngine? = null
+    val handler = Handler(Looper.getMainLooper())
+
+    private fun initView(type: Int, uid: Int) {
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            // 在这里执行与UI元素相关的操作
+            try {
+                if (type==1){
+                    initializeAndJoinChannel()
+                }else if (type==2){
+                    setupRemoteVideo(uid)
+                }
+            } catch (e: Exception) {
+
+            }
+        }
+
+    }
+
+
+    private val mRtcEventHandler: IRtcEngineEventHandler = object : IRtcEngineEventHandler() {
+        // 监听频道内的远端用户,获取用户的 uid 信息
+        override fun onUserJoined(uid: Int, elapsed: Int) {
+                initView(2, uid)
+
+        }
+    }
+
+    private fun initializeAndJoinChannel() {
+        try {
+            // 创建 RtcEngineConfig 对象,并进行配置
+            val config = RtcEngineConfig()
+            config.mContext = context
+            config.mAppId = appId
+            config.mEventHandler = mRtcEventHandler
+            // 创建并初始化 RtcEngine
+            mRtcEngine = RtcEngine.create(config)
+        } catch (e: Exception) {
+            throw RuntimeException("Check the error.")
+        }
+
+        mRtcEngine?.setVideoEncoderConfiguration(VideoEncoderConfiguration(
+                VideoEncoderConfiguration.VD_1920x1080,
+                VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
+                VideoEncoderConfiguration.STANDARD_BITRATE,
+                VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT))
+        // 启用视频模块
+        mRtcEngine?.enableVideo()
+        // 开启本地预览
+        mRtcEngine?.startPreview()
+
+
+        // 创建 ChannelMediaOptions 对象,并进行配置
+        val options = ChannelMediaOptions()
+        // 根据场景将用户角色设置为 BROADCASTER (主播) 或 AUDIENCE (观众)
+        options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
+        // 直播场景下,设置频道场景为 BROADCASTING (直播场景)
+        options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
+        // 使用临时 Token 加入频道,自行指定用户 ID 并确保其在频道内的唯一性
+        mRtcEngine?.joinChannel(token, channelName, uuid.toInt(), options)
+    }
+
+    private fun setupRemoteVideo(uid: Int) {
+        val surfaceView = SurfaceView(context)
+        surfaceView.setZOrderMediaOverlay(true)
+        remote_video_view_container.addView(surfaceView)
+        // 将 SurfaceView 对象传入声网实时互动 SDK,设置远端视图
+        mRtcEngine!!.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))
+
+        val surfaceView2 = SurfaceView(context)
+        surfaceView2.setZOrderMediaOverlay(true)
+        local_video_view_user.addView(surfaceView2)
+        local_video_view_user.bringToFront()
+        // 将 SurfaceView 对象传入声网实时互动 SDK,设置本地视图
+        mRtcEngine?.setupLocalVideo(VideoCanvas(surfaceView2, VideoCanvas.VIEW_SETUP_MODE_ADD, 0))
+
+
+    }
+
+
+    /**
+     *显示数据
+     */
+    override fun showData(data: ArrayList<BroadcastDO>) {
+    }
+
+    /**
+     *处理错误信息
+     */
+    override fun onError(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载完成
+     */
+    override fun complete(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载开始
+     */
+    override fun start() {
+    }
+
+    /**
+     *处理网络状态
+     */
+    override fun networkMonitor(state: NetState) {
+    }
+    override fun onStart() {
+        super.onStart()
+        if (!EventBus.getDefault().isRegistered(this)) {
+            // 未注册的逻辑处理
+            EventBus.getDefault().register(this)
+        }
+
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (EventBus.getDefault().isRegistered(this)) {
+            // 已注册的逻辑处理
+            EventBus.getDefault().unregister(this)
+        }
+        RingPlayHelper.stopRingTone()
+        //停止本地视频预览
+        mRtcEngine?.stopPreview();
+        //离开频道
+        mRtcEngine?.leaveChannel();
+        is_bed_user =false
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if(!SettingConfig.getSwCall(activity)){
+            cancelTimer()
+        }
+        visit_voice_call_timer.stop()
+
+    }
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+
+    }
+    //计时器
+    lateinit var countDownTimer: CountDownTimer
+    //初始化计时器
+    protected fun initCountDownTimer(view: TextView) {
+        val overTime = SettingConfig.getSipOverTime(activity) * 1000L
+        countDownTimer = object: CountDownTimer(overTime, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+                if (view != null) {
+                    val time = millisUntilFinished/1000
+                    val timeText = getString(R.string.countdown_time, time)
+                    view.setText(timeText)
+                }
+            }
+
+            override fun onFinish() {
+                //呼叫超时,返回到主界面
+                RingPlayHelper.stopRingTone()
+                showMessage(R.string.no_response)
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                val rejectTcp = SwVisitUtil.BedvisitRemote(channelName)
+                TcpClient.getInstance().sendMsg(rejectTcp.toJson())
+                EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+            }
+        }
+    }
+
+    //开始计时
+    private fun startTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.start()
+        }
+    }
+
+    //取消计时器
+    private fun cancelTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.cancel()
+        }
+    }
+}

+ 1 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/hardware/imp/WdchHardTools.java

@@ -100,6 +100,7 @@ public class WdchHardTools extends HardTools {
 //            SerialPortHelper.setMIC(false);
 //            SerialPortHelper.setHandsFree(true);
             SerialPortHelper.setSosLight("0");
+//            NfcUtils.getInstance().NfcOnResume();
             // 默认门灯白色
             // SerialPortHelper.setDoorLight(1, "111");
         } catch (InterruptedException e) {

+ 11 - 1
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/settings/SettingConfig.java

@@ -92,7 +92,6 @@ public class SettingConfig {
     //是否开启床垫服务
     private static final String KEY_SP_SLEEP_GATEWAY = "KEY_SP_SLEEP_GATEWAY";
 
-
     //是否设备场景类型 //医院 月子中心 养老 公寓
     private static final String KEY_SP_Scene_type= "KEY_SP_Scene_type";
 
@@ -102,6 +101,17 @@ public class SettingConfig {
     //屏幕方向
     private static final String KEY_SP_window= "KEY_SP_window";
 
+    //是否开启声网自动接听
+    private static final String KEY_SP_SW_CALL = "KEY_SP_SW_CALL";
+
+    public static void setSwCall(Context context, boolean on) {
+        getEditor(context).putBoolean(KEY_SP_SW_CALL, on).apply();
+    }
+
+    public static boolean getSwCall(Context context) {
+        return getSP(context).getBoolean(KEY_SP_SW_CALL, true);
+    }
+
     public static void setScene(Context context, int mode) {
         getEditor(context).putInt(KEY_SP_Scene_type, mode).apply();
     }

+ 9 - 0
android_bed/src/main/res/drawable/shape_visit_bg.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#F4F6F9"/>
+    <stroke android:width="2dp" android:color="#0090ff" />
+    <corners
+        android:radius="5dp"
+      />
+
+</shape>

+ 101 - 0
android_bed/src/main/res/layout/adapter_visit_frame_part.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/rl_bed_info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/shape_visit_bg"
+        android:layout_marginBottom="@dimen/d15"
+        android:paddingBottom="@dimen/d20">
+
+        <TextView
+            android:id="@+id/visit_region"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d20"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="探视床位:"
+            android:textColor="@color/black"
+            android:visibility="gone"
+            android:textSize="@dimen/font_size_18" />
+        <TextView
+            android:id="@+id/visit_region_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d20"
+            android:layout_toRightOf="@+id/visit_region"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="张芳鹏 32床"
+            android:visibility="gone"
+            android:textStyle="bold"
+            android:textColor="@color/color_red"
+            android:textSize="@dimen/font_size_18" />
+
+
+        <TextView
+            android:id="@+id/visit_name_tit"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d8"
+            android:text="探视人:"
+            android:textSize="@dimen/font_size_18"
+            android:layout_below="@+id/visit_region"
+            android:textColor="@color/black" />
+        <TextView
+            android:id="@+id/visit_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_toRightOf="@+id/visit_name_tit"
+            android:layout_marginTop="@dimen/d8"
+            android:text="张宏伟"
+            android:textStyle="bold"
+            android:textSize="@dimen/font_size_18"
+            android:layout_below="@+id/visit_region"
+            android:textColor="@color/orange" />
+        <TextView
+            android:id="@+id/visit_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d8"
+            android:layout_below="@+id/visit_region"
+            android:layout_toRightOf="@+id/visit_name"
+            android:text="关系 :父亲"
+            android:textSize="@dimen/font_size_18"
+            android:textStyle="bold"
+            android:textColor="@color/color_main" />
+        <TextView
+            android:id="@+id/visit_sk_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="@dimen/d40"
+            android:text="已完成"
+            android:textSize="@dimen/font_size_40"
+            android:textStyle="bold"
+            android:visibility="gone"
+            android:textColor="@color/color_main" />
+
+
+        <TextView
+            android:id="@+id/visit_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d5"
+            android:layout_below="@+id/visit_name"
+            android:text="探视时间:每天10:00~12:00"
+            android:textSize="@dimen/font_size_18"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="@color/black" />
+
+    </RelativeLayout>
+</layout>

+ 145 - 0
android_bed/src/main/res/layout/fragment_visit_agore_call.xml

@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@mipmap/call_bg">
+
+
+
+        <!--全屏视频画面-->
+        <FrameLayout
+            android:id="@+id/remote_video_view_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            />
+
+
+        <LinearLayout
+            android:id="@+id/visit_call_bt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="@dimen/d50"
+            android:layout_centerHorizontal="true"
+            android:visibility="gone"
+            >
+            <Chronometer
+                android:id="@+id/visit_voice_call_timer"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:text="00:00"
+                android:textColor="@color/white"
+                android:textSize="24sp" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="32dp">
+
+                <ImageView
+                    android:id="@+id/visit_voice_call_mute"
+                    android:layout_width="84dp"
+                    android:layout_height="84dp"
+                    android:layout_marginRight="40dp"
+                    android:clickable="true"
+                    android:src="@drawable/av_mute_selector"
+                   />
+
+
+                <ImageView
+                    android:id="@+id/visit_voice_call_hangup"
+                    android:layout_width="80dp"
+                    android:layout_height="80dp"
+                    android:layout_marginLeft="@dimen/d20"
+                    android:clickable="true"
+                    android:src="@drawable/selector_call_hangup" />
+
+                <ImageView
+                    android:id="@+id/visit_call_speaker"
+                    android:layout_width="84dp"
+                    android:layout_height="84dp"
+                    android:layout_marginStart="40dp"
+                    android:clickable="true"
+                    android:src="@drawable/av_speaker_selector"
+                    />
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <!--来电-->
+        <LinearLayout
+            android:id="@+id/visit_call_incoming"
+            android:layout_width="match_parent"
+            android:layout_height="180dp"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:layout_marginBottom="100dp"
+            android:gravity="bottom"
+            android:orientation="vertical"
+            >
+            <Chronometer
+                android:id="@+id/visit_voice_call_timer2"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:text="00:00"
+                android:textColor="@color/white"
+                android:textSize="24sp" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="horizontal"
+                android:layout_marginTop="@dimen/d40"
+
+                >
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="2" />
+
+                <ImageView
+                    android:id="@+id/visit_call_ring_reject"
+                    android:layout_width="100dp"
+                    android:layout_height="100dp"
+                    android:src="@drawable/selector_call_hangup" />
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="1" />
+
+                <ImageView
+                    android:id="@+id/visit_call_ring_pickup_audio"
+                    android:layout_width="100dp"
+                    android:layout_height="100dp"
+                    android:src="@drawable/selector_call_answer" />
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="2" />
+            </LinearLayout>
+
+
+        </LinearLayout>
+
+        <!--小窗视频画面-->
+        <FrameLayout
+            android:id="@+id/local_video_view_user"
+            android:layout_width="200dp"
+            android:layout_height="240dp"
+            android:layout_gravity="top|end"
+            android:layout_marginHorizontal="10dp"
+            android:layout_marginTop="10dp"
+            />
+
+
+    </RelativeLayout>
+</layout>

+ 184 - 0
android_bed/src/main/res/layout/visit_list_lay.xml

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:background="#EAF2F9">
+
+        <include
+            android:id="@+id/activity_calling_door_layout_title"
+            layout="@layout/view_title_layout" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:padding="@dimen/d22"
+            >
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/d80"
+                android:orientation="horizontal">
+
+                <RelativeLayout
+                    android:layout_width="0dp"
+                    android:layout_height="72dp"
+                    android:layout_weight="0.8"
+                    android:orientation="horizontal"
+                    android:background="@drawable/shape_bed_bg">
+
+                    <TextView
+                        android:id="@+id/bed_sw_visit_name"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="--"
+                        android:textSize="@dimen/font_size_18"
+                        android:textColor="@color/black"
+                        android:textStyle="bold"
+                        android:layout_marginTop="@dimen/d24"
+                        android:layout_marginLeft="@dimen/d25" />
+
+                    <TextView
+                        android:id="@+id/bed_sw_visit_sex"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_toRightOf="@+id/bed_sw_visit_name"
+                        android:text="-"
+                        android:textSize="@dimen/font_size_18"
+                        android:textColor="@color/black"
+                        android:textStyle="bold"
+                        android:layout_marginTop="@dimen/d24"
+                        android:layout_marginLeft="@dimen/d10" />
+
+                    <TextView
+                        android:id="@+id/bed_sw_visit_age"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_toRightOf="@+id/bed_sw_visit_sex"
+                        android:text="-"
+                        android:textSize="@dimen/font_size_18"
+                        android:textColor="@color/black"
+                        android:textStyle="bold"
+                        android:layout_marginTop="@dimen/d24"
+                        android:layout_marginLeft="@dimen/d15" />
+
+
+
+                </RelativeLayout>
+
+                <RelativeLayout
+                    android:layout_width="240dp"
+                    android:layout_height="72dp"
+                    android:orientation="horizontal"
+                    android:layout_marginLeft="@dimen/d10"
+                    android:layout_marginRight="@dimen/d10"
+                    android:gravity="center"
+                    android:background="@drawable/shape_bed_bg">
+
+                    <TextView
+                        android:id="@+id/switch_tx"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:textSize="20sp"
+                        android:textColor="@color/black"
+                        android:layout_marginLeft="@dimen/d20"
+                        android:text="自动接听"/>
+
+                    <Switch
+                        android:id="@+id/bed_sw_visit_switch"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_alignParentRight="true"
+                        android:layout_marginRight="@dimen/d20"
+                        android:switchMinWidth="40dp"
+                        android:checked="true"
+                        android:thumb="@drawable/thumb_selector"
+                        android:track="@drawable/track_selector"/>
+
+                </RelativeLayout>
+
+            </LinearLayout>
+
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical"
+                android:layout_marginTop="@dimen/d20"
+                android:padding="@dimen/d20"
+                android:background="@drawable/shape_bed_bg"
+                >
+                <RelativeLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/shape_bed_bg">
+
+                    <ImageView
+                        android:id="@+id/bed_sw_visit_img_top"
+                        android:layout_width="40dp"
+                        android:layout_height="40dp"
+                        android:layout_alignParentLeft="true"
+                        android:src="@mipmap/img_x" />
+
+                    <TextView
+                        android:id="@+id/bed_sw_visit_text_top"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_centerVertical="true"
+                        android:layout_marginLeft="@dimen/d20"
+                        android:layout_toRightOf="@+id/bed_sw_visit_img_top"
+                        android:text="探视列表"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_18"
+                        android:textStyle="bold" />
+
+
+                </RelativeLayout>
+
+                <ImageView
+                    android:id="@+id/bed_sw_visit_emptyImageView"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:src="@mipmap/no_information"
+                    android:layout_gravity="center"
+                    android:visibility="gone"
+                    />
+                    <com.scwang.smartrefresh.layout.SmartRefreshLayout
+                        android:id="@+id/bed_sw_visit_refresh"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:background="@color/white"
+
+                        bind:srlEnableLoadMore="true"
+                        bind:srlEnableRefresh="true">
+                        <androidx.recyclerview.widget.RecyclerView
+                            android:id="@+id/bed_sw_visit_recycler"
+                            android:layout_width="match_parent"
+                            android:layout_height="match_parent"
+                            android:layout_marginTop="@dimen/d15"
+                            android:background="@color/white"
+                            android:clipChildren="true" />
+                    </com.scwang.smartrefresh.layout.SmartRefreshLayout>
+
+
+
+            </LinearLayout>
+
+
+        </LinearLayout>
+
+
+
+
+
+
+
+
+
+
+
+    </LinearLayout>
+</layout>

+ 5 - 7
android_host/build.gradle

@@ -32,15 +32,10 @@ android {
         buildConfigField "String", "BUILD_TIME", getDate()
         buildConfigField 'String', 'VERSION_NAME', "\"${project.rootProject.ext.app_version}\""
         buildConfigField 'String', 'VERSION_CODE', "\"${project.rootProject.ext.app_version_code}\""
-        buildConfigField 'String', 'iscallingdoor', "\"${project.rootProject.ext.callingdoor}\""
-        buildConfigField 'String', 'isandroid_bed', "\"${project.rootProject.ext.android_bed}\""
         buildConfigField 'String', 'isandroid_host', "\"${project.rootProject.ext.android_host}\""
-        buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
-        buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
-        buildConfigField 'String', 'open_sleep', "\"${project.rootProject.ext.open_sleep}\""
-        buildConfigField 'String', 'open_433', "\"${project.rootProject.ext.open_433}\""
         buildConfigField 'String', 'device_type', "\"${project.rootProject.ext.device_type}\""
+        buildConfigField 'String', 'is_agora', "\"${project.rootProject.ext.is_agora}\""
     }
 
     productFlavors {
@@ -129,12 +124,15 @@ dependencies {
     //广播喊话组件
     compile project(':gstream')
 
+
+
+
     compile 'com.contrarywind:Android-PickerView:4.1.9'
 
     implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
 
 
-
+    implementation 'io.agora.rtc:full-sdk:4.2.6'
 }
 
 /**

+ 7 - 0
android_host/src/main/AndroidManifest.xml

@@ -62,6 +62,12 @@
         tools:ignore="ProtectedPermissions" />
     <!--    音量权限-->
     <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
+    <!--    声网权限-->
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <!-- 对于 Android 12.0 及以上且集成 v4.1.0 以下 SDK 的设备,还需要添加以下权限 -->
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+    <!-- 对于 Android 12.0 及以上设备,还需要添加以下权限 -->
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
 
     <application
         android:allowBackup="true"
@@ -114,6 +120,7 @@
 
         <activity android:name=".activity.SipTestActivity" />
         <activity android:name=".activity.CallActivity" />
+        <service android:name=".visit.VisitService"/>
 
         <service
             android:name=".service.WdklSipService"

+ 182 - 24
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt

@@ -28,11 +28,13 @@ import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.google.common.base.Strings
 import com.google.gson.Gson
-import com.wdkl.gstreamer.demo.MyGStreamManager
 import com.wdkl.ncs.android.component.nursehome.BuildConfig
 import com.wdkl.ncs.android.component.nursehome.R
 import com.wdkl.ncs.android.component.nursehome.databinding.ActivityNewNurseHomeBinding
 import com.wdkl.ncs.android.component.nursehome.fragment.*
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitCallFragment
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitMainFragment
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitSetFragment
 import com.wdkl.ncs.android.component.nursehome.hardware.HardWareFactroy
 import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
 import com.wdkl.ncs.android.component.nursehome.led.LedItem
@@ -40,6 +42,7 @@ import com.wdkl.ncs.android.component.nursehome.led.LedManagerUtils
 import com.wdkl.ncs.android.component.nursehome.service.WdklSipService
 import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.nursehome.util.*
+import com.wdkl.ncs.android.component.nursehome.visit.tcp.VisitTcpClient
 import com.wdkl.ncs.android.component.nursehome.window.IncidentWindow
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.base.BaseApplication
@@ -61,11 +64,7 @@ import com.wdkl.ncs.android.middleware.model.vo.HostDevicePartSettingVO
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
 import com.wdkl.ncs.android.middleware.model.vo.NurseDeviceInfoVO
 import com.wdkl.ncs.android.middleware.tcp.TcpClient
-import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
-import com.wdkl.ncs.android.middleware.tcp.channel.EntraceGuardUtil
-import com.wdkl.ncs.android.middleware.tcp.channel.OtherUtil
-import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
-import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback
+import com.wdkl.ncs.android.middleware.tcp.channel.*
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
 import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
@@ -76,12 +75,12 @@ import com.wdkl.ncs.janus.util.JanusConstant
 import kotlinx.android.synthetic.main.activity_new_nurse_home.*
 import kotlinx.android.synthetic.main.activity_nurse_home.*
 import kotlinx.android.synthetic.main.activity_nurse_home.view_title_layout_tv_point
+import kotlinx.android.synthetic.main.adapter_broadcasting_frame_part.*
 import kotlinx.android.synthetic.main.view_title_layout.*
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 import org.linphone.core.AccountCreator
-import org.linphone.core.Core
 import org.linphone.core.RegistrationState
 import org.linphone.core.TransportType
 import serialporttest.utils.SerialPortUtilHost
@@ -119,10 +118,23 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
     var batteryBroadcastReceiver: BatteryBroadcastReceiver? = null
     private var executor: ScheduledExecutorService? = null
 
+    private var visIntent: Intent? = null
+
 
     companion object {
         var sosItemList = ArrayList<CallingItem>()
         var callingList = ArrayList<CallingItem>()
+        var viditingList = ArrayList<String>()
+
+        // 添加字符串到 viditingList 中
+        fun addToViditingList(str: String) {
+            viditingList.add(str)
+        }
+
+        // 删除 viditingList 中与指定值相同的字符串
+        fun removeValueFromViditingList(str: String) {
+            viditingList.removeAll { it == str }
+        }
 
         fun checkIncomingCall(bedDeviceId: Int?): Boolean {
             if (callingList.size > 0) {
@@ -159,6 +171,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
     private val uninstallApk = false
     private var copyDone = false
     private var tcpConnect: Boolean = false
+    private var visittcpConnect: Boolean = false
 
     private var isFragmentVisible = false
 
@@ -537,11 +550,17 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 }
 
                 R.id.menu_led -> {
-                    showMiddleFragment(LedSettingsFragment())
+//                    val fragment: Fragment? = JRouter.prepare().create("/agora/visit").seek() as Fragment?
+//                    val fragment: Fragment? = JRouter.prepare().create("/agora/visit").
+                    showMiddleFragment(VisitMainFragment())
+                    redDot.visibility = View.GONE
                 }
             }
         }
-
+        menu_led.setOnClickListener {
+            showMiddleFragment(VisitMainFragment())
+            redDot.visibility = View.GONE
+        }
         main_rl_3.setOnClickListener(this)
     }
     /**
@@ -720,6 +739,11 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         Log.e(TAG, "收到返回的设置配置信息 ")
         Log.e(TAG, "partId " + data.partId)
 
+        //寻找 这里要做判断 没有声网探视不启动
+        Thread {
+            VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL_text, Constant.VISIT_TCP_PORT!!.toInt(), Constant.reader_idle_time!!.toInt())
+        }.start()
+
         if (SettingConfig.getSipEnabled(activity)) {
             //配置sip账户
             if (WdklSipService.getCore() != null) {
@@ -818,6 +842,19 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             }
 
             setSettingConfiguration()
+//            if (data.isBool_outside_vital && !visittcpConnect) {
+//                visittcpConnect = true
+//                //初始化tcp
+//                Thread {
+//                    if (!BuildConfig.DEBUG){
+//                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL, Constant.VISIT_TCP_PORT!!.toInt(), Constant.reader_idle_time!!.toInt())
+//                    }else{
+//                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL_text, Constant.VISIT_TCP_PORT!!.toInt(), Constant.reader_idle_time!!.toInt())
+//                    }
+//                    }.start()
+//            }
+
+
         } catch (e: Exception) {
             //showMessage("Setting configs error: " + e.message)
             Log.i(TAG, "获取设置配置信息异常" + e.message)
@@ -923,7 +960,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         supportFragmentManager.inTransaction {
             remove(currentFragment!!)
             currentFragment = fragment
-            //updateLeftBtState()
+            updateLeftBtState()
             add(R.id.host_main_frame, fragment)
         }
     }
@@ -1446,6 +1483,11 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 menu_more.setBackgroundResource(R.drawable.shape_main_bt_bg2)
                 menu_more.setTextColor(getResources().getColor(R.color.delete_text_color));
             }
+            is VisitMainFragment -> {
+                //led
+                menu_led.setBackgroundResource(R.drawable.shape_main_bt_bg2)
+                menu_led.setTextColor(getResources().getColor(R.color.delete_text_color));
+            }
         }
 
     }
@@ -1513,9 +1555,9 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                         val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                         //视频通话请求
                         if (Constant.CALL_STATE == Constant.CALL_CALLING
-                            || Constant.CALL_STATE == Constant.CALL_OUTGOING
-                            || Constant.CALL_STATE == Constant.CALL_VISITING
-                            || Constant.CALL_STATE == Constant.CALL_V_INCOMING) {
+                                || Constant.CALL_STATE == Constant.CALL_OUTGOING
+                                || Constant.CALL_STATE == Constant.CALL_VISITING
+                                || Constant.CALL_STATE == Constant.CALL_V_INCOMING) {
                             //通话占线
                             val callTcp = VoiceUtil.voiceCalling(tcpModel.tid, Constant.DEVICE_ID, tcpModel.fromId, interactionVO.id)
                             TcpClient.getInstance().sendMsg(callTcp.toJson())
@@ -1528,8 +1570,8 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                             if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
                                 var frameName: String? = ""
                                 if (DeviceTypeEnum.DOCTOR_HOST.value() == interactionVO.fromDeviceType
-                                    || DeviceTypeEnum.NURSE_HOST.value() == interactionVO.fromDeviceType
-                                    || DeviceTypeEnum.OTHER_HOST.value() == interactionVO.fromDeviceType) {
+                                        || DeviceTypeEnum.NURSE_HOST.value() == interactionVO.fromDeviceType
+                                        || DeviceTypeEnum.OTHER_HOST.value() == interactionVO.fromDeviceType) {
                                     //医生机,护士主机,其他主机,总控主机等
                                     frameName = interactionVO.fromDeviceName
                                 } else if (DeviceTypeEnum.NURSE_WATCH.value() == interactionVO.fromDeviceType) {
@@ -2055,9 +2097,9 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                         LedHelper.updateLedInfo(interactionVO, true, false)
                         showSosAlert(tcpModel, false, false)
                         if (Constant.CALL_STATE != Constant.CALL_CALLING
-                            && Constant.CALL_STATE != Constant.CALL_OUTGOING
-                            && Constant.CALL_STATE != Constant.CALL_VISITING
-                            && Constant.CALL_STATE != Constant.CALL_V_INCOMING
+                                && Constant.CALL_STATE != Constant.CALL_OUTGOING
+                                && Constant.CALL_STATE != Constant.CALL_VISITING
+                                && Constant.CALL_STATE != Constant.CALL_V_INCOMING
                         ) {
                             if (SettingConfig.getTtsMode(this) == SettingConfig.TTS_ON) {
                                 val frameName: String
@@ -2139,6 +2181,94 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
 
                     }
+                } else if (tcpModel.type == TcpType.REMOTE_VISIT) {
+                    if (tcpModel.action == TcpAction.REMOTEVISITAction.CALL) {
+                        //通知分机进入探视
+                        val json = JSON.parseObject(tcpModel.data.toString())
+//                        val bedId = json.getString("bed")
+                        val visitid = json.getString("id")
+                        val toId = json.getString("callBedId")
+                        val callTcp = SwVisitUtil.visitCall(Constant.DEVICE_ID,toId.toInt() ,visitid)
+                        TcpClient.getInstance().sendMsg(callTcp.toJson())
+                        viditingList.add(visitid.toString())
+                        EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                    } else if (tcpModel.action == TcpAction.REMOTEVISITAction.FAILED) {
+                        //收到 分机不在线,通知界面,转接给小程序服务器
+                        val visitid = tcpModel.data.toString()
+                        val callTcp = SwVisitUtil.visitHandoff( visitid)
+                        VisitTcpClient.getInstance().sendMsg(callTcp.toJson())
+                        removeValueFromViditingList(visitid.toString())
+                        if (skyCallFragment is VisitCallFragment){
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                        }else{
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                        }
+                    }else if (tcpModel.action == TcpAction.REMOTEVISITAction.CANCEL) {
+                        //收到 小程序取消 通知页面 比对id,取消探视(或者刷新页面)直接转给分机
+//                        val json = JSON.parseObject(tcpModel.data.toString())
+                        val visitid = tcpModel.data.toString()
+                        val callTcp = SwVisitUtil.visitCancel(Constant.DEVICE_ID,visitid)
+                        TcpClient.getInstance().sendMsg(callTcp.toJson())
+                        removeValueFromViditingList(visitid.toString())
+                        //寻找 需要补充一个如果挂断提示,如果正在查看的情况下
+//                        showMessage("通话已取消.....")
+                        if (skyCallFragment is VisitCallFragment){
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                        }else{
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                        }
+
+                    }else if (tcpModel.action == TcpAction.REMOTEVISITAction.HANDOFF) {
+                        //收到 挂断 fromId不为null的是分机发过来的,为null的是 直接转给分机并且通知主机界面关闭
+                        if(tcpModel.tid!=null){
+                            //分机发过来的
+                            val visitid = tcpModel.data.toString()
+                            val callTcp = SwVisitUtil.visitWxHANDOFF(visitid)
+                            VisitTcpClient.getInstance().sendMsg(callTcp.toJson())
+                            removeValueFromViditingList(visitid.toString())
+                        }else{
+                            //小程序发过来的
+                            val visitid = tcpModel.data.toString()
+                            val callTcp = SwVisitUtil.visitHangup(Constant.DEVICE_ID,visitid)
+                            TcpClient.getInstance().sendMsg(callTcp.toJson())
+                            removeValueFromViditingList(visitid.toString())
+                        }
+                        //寻找 需要补充一个如果挂断提示,如果正在查看的情况下
+//                        showMessage("通话已挂断.....")
+                        if (skyCallFragment is VisitCallFragment){
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                        }else{
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                        }
+                    }else if (tcpModel.action == TcpAction.REMOTEVISITAction.DATA) {
+                        //收到 通知刷新预约界面,左侧消息小红点。
+                        redDot.visibility = View.VISIBLE
+                        EventBus.getDefault().post(MessageEvent("refresh", Constant.EVENT_VISIT_list))
+                    }else if (tcpModel.action == TcpAction.REMOTEVISITAction.CALLING) {
+                        //收到 通知小程序 分机正在通话中
+                        val visitid = tcpModel.data.toString()
+                        val callTcp = SwVisitUtil.visitIngCall(visitid)
+                        VisitTcpClient.getInstance().sendMsg(callTcp.toJson())
+                        //寻找 需要补充一个如果挂断提示,如果正在查看的情况下
+//                        showMessage("通话异常.....")
+                        removeValueFromViditingList(visitid.toString())
+                        if (skyCallFragment is VisitCallFragment){
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                        }else{
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                        }
+                    }else if (tcpModel.action == TcpAction.REMOTEVISITAction.REJECT) {
+                        //收到 通知小程序 分机拒绝
+                        val visitid = tcpModel.data.toString()
+                        val callTcp = SwVisitUtil.visitreject(visitid)
+                        VisitTcpClient.getInstance().sendMsg(callTcp.toJson())
+                        removeValueFromViditingList(visitid.toString())
+                        if (skyCallFragment is VisitCallFragment){
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+                        }else{
+                            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_VISIT_list))
+                        }
+                    }
                 }
 
 
@@ -2185,11 +2315,6 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 AppUpdateHelper.restartApp(activity)
             }
 
-            Constant.EVENT_r_br_set -> {
-                showMiddleFragment(BroadcastNewFragment())
-
-            }
-
             Constant.EVENT_BROADCAST_STATE -> {
                 val state = messageEvent.getMessage() as String
                 if ("on".equals(state)) {
@@ -2198,10 +2323,13 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                     tv_broadcast_state.visibility = View.GONE
                 }
             }
+            Constant.EVENT_r_br_set -> {
+                showMiddleFragment(BroadcastNewFragment())
+
+            }
 
             Constant.EVENT_V_br_set -> {
                 supportFragmentManager.inTransaction {
-
                     val bundle = Bundle()
                     bundle.putString("id", messageEvent.message.toString())
                     val fragment = BroadcastSetFragment()
@@ -2212,6 +2340,36 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 }
 
             }
+
+            Constant.EVENT_r_VISIT_th -> {
+                showMiddleFragment(VisitMainFragment())
+
+            }
+            Constant.EVENT_V_VISIT_th -> {
+                supportFragmentManager.inTransaction {
+                    val bundle = Bundle()
+                    bundle.putString("id", messageEvent.message.toString())
+                    bundle.putString("channelName", messageEvent.channelName.toString())
+                    val fragment = VisitSetFragment()
+                    remove(currentFragment!!)
+                    currentFragment = fragment
+                    fragment.arguments = bundle
+                    add(R.id.host_main_frame, fragment)
+                }
+
+            }
+
+
+            //进入院外探视
+            Constant.EVENT_VISIT_START -> {
+                var fragment = VisitCallFragment()
+                var bundle = Bundle()
+                bundle.putString("channelName", messageEvent.channelName.toString())
+                bundle.putString("type", messageEvent.message.toString())
+                fragment.arguments = bundle
+                addCallFragment(fragment)
+            }
+
         }
 
     }

+ 96 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/VisitGroupAdapter.kt

@@ -0,0 +1,96 @@
+package com.wdkl.ncs.android.component.nursehome.adapter
+
+import android.content.Context
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.middleware.model.vo.*
+
+class VisitGroupAdapter : RecyclerView.Adapter<VisitGroupAdapter.ViewHolder>{
+
+    private var context: Context
+    private var data: ArrayList<FrameBedVO>
+    private lateinit var onItemClickListener: OnItemClickListener
+    private var selectedItemPosition = RecyclerView.NO_POSITION //
+    // 初始值为RecyclerView中的一个无效位置
+    constructor(context: Context, data: ArrayList<FrameBedVO>): super() {
+        this.context = context
+        this.data = data
+    }
+
+    fun setOnItemClickListener(listener: OnItemClickListener) {
+        this.onItemClickListener = listener
+    }
+
+    fun updateData(data: ArrayList<FrameBedVO>) {
+        this.data = data
+        notifyDataSetChanged()
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent.context).inflate(R.layout.visit_set_item_lay, parent, false)
+        val viewHolder = ViewHolder(view)
+
+        return viewHolder
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val currentItem = data[position]
+        if ( data.get(position).frameBed.fullName!=null){
+            holder.visitmainitembedname .text= data.get(position).frameBed.fullName
+            holder.signitemname.text = data.get(position).customerName
+        }
+        // 根据选中状态设置背景和文字颜色
+        if (position == selectedItemPosition) {
+            holder.visitmainitembedll.setBackgroundResource(R.drawable.shape_time_f_bg_d)
+            holder.signitemname.setTextColor(Color.parseColor("#0090ff"))
+        } else {
+            holder.visitmainitembedll.setBackgroundResource(R.drawable.shape_time_f_bg)
+            holder.signitemname.setTextColor(Color.parseColor("#000000"))
+        }
+
+        holder.signitemname.setOnClickListener {
+            // 更新选中的位置
+            val previousSelectedPosition = selectedItemPosition
+            selectedItemPosition = holder.adapterPosition
+
+            // 通知RecyclerView刷新选中的item和之前选中的item
+            notifyItemChanged(selectedItemPosition)
+            if (previousSelectedPosition != RecyclerView.NO_POSITION) {
+                notifyItemChanged(previousSelectedPosition)
+            }
+            val ids = data.get(position).bedDeviceId
+            onItemClickListener.onItemClick(holder.signitemname, position,ids.toString())
+        }
+    }
+
+     fun updateSelectedPosition(newPosition: Int) {
+
+        notifyDataSetChanged()
+    }
+
+    class ViewHolder: RecyclerView.ViewHolder {
+        var signitemname : TextView
+        var visitmainitembedname : TextView
+        var visitmainitembedll : LinearLayout
+
+        constructor(itemView: View): super(itemView) {
+            signitemname = itemView.findViewById(R.id.visit_main_item_name)
+            visitmainitembedname = itemView.findViewById(R.id.visit_main_item_bed_name)
+            visitmainitembedll = itemView.findViewById(R.id.visit_main_item_bed_ll)
+        }
+    }
+
+    interface OnItemClickListener {
+        fun onItemClick(view: View, keyId: Int,ids:String)
+    }
+}

+ 149 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/VisitListAdapter.kt

@@ -0,0 +1,149 @@
+package com.wdkl.ncs.android.component.nursehome.adapter
+
+import android.graphics.Color
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
+import com.wdkl.ncs.android.component.nursehome.databinding.AdapterVisitFramePartBinding
+
+
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.middleware.model.vo.VisitPageVo
+
+/**
+ * 广播列表适配器
+ */
+class VisitListAdapter(val data: ArrayList<VisitPageVo.DataBean>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterVisitFramePartBinding>, VisitPageVo.DataBean>() {
+
+    private lateinit var onItemClickListener: OnItemClickListener
+
+
+
+    fun setOnItemClickListener(listener: OnItemClickListener) {
+        this.onItemClickListener = listener
+    }
+    /**
+     * 数据提供者
+     */
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    /**
+     * Item坐标
+     */
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    /**
+     * 获取Item总数
+     */
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    /**
+     * 创建LayoutHelper
+     */
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        return LinearLayoutHelper(0, data.size)
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterVisitFramePartBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_visit_frame_part)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterVisitFramePartBinding>, position: Int) {
+        holder?.bind { binding ->
+            try {
+                val itemData = getItem(position)
+
+
+                binding.visitName.text = itemData.member_name
+                binding.visitType.text = "关系:"+itemData.relation
+                binding.visitRegionTxt.text =itemData.customer_name +"  "+itemData.bed_no+"床"
+                binding.visitTime.text = TimeHandle.getDateTime(itemData.start_time * 1000, "yyyy.MM.dd HH:mm")+"-"+TimeHandle.getDateTime(itemData.end_time * 1000, "yyyy.MM.dd HH:mm")
+
+                binding.visitDelete.visibility=View.GONE
+                binding.visitTing.visibility=View.GONE
+                binding.visitPlay.visibility=View.GONE
+                binding.visitStop.visibility=View.GONE
+                binding.visitSettings.visibility=View.GONE
+                binding.visitSkType.visibility=View.GONE
+                if (itemData.status==0){
+                    //审核中
+                    binding.visitStop.visibility=View.VISIBLE
+                    binding.visitSettings.visibility=View.VISIBLE
+                }else if (itemData.status==1){
+                    binding.visitTing.visibility=View.VISIBLE
+//                    binding.visitPlay.visibility=View.VISIBLE
+                }else if (itemData.status==2 && extraParam.equals("")){
+                    binding.visitSkType.visibility=View.VISIBLE
+                    binding.visitSkType.text="已拒绝"
+                    binding.visitSkType.setTextColor(Color.parseColor("#F65F5F"))
+                }else if (itemData.status==3 && extraParam.equals("")){
+                    binding.visitSkType.visibility=View.VISIBLE
+                    binding.visitSkType.text="已完成"
+                    binding.visitSkType.setTextColor(Color.parseColor("#2F9DF1"))
+                }
+                //删除
+                binding.visitDelete.setOnClickListener {
+                    onItemClickListener.onItemClick(itemData.id,"删除")
+                }
+                //接听
+                binding.visitTing.setOnClickListener {
+                    onItemClickListener.onItemClick(itemData.id,"查看")
+                }
+                //挂断
+                binding.visitPlay.setOnClickListener {
+                    onItemClickListener.onItemClick(itemData.id,"挂断")
+                }
+                //通过
+                binding.visitStop.setOnClickListener {
+                    onItemClickListener.onItemClick(itemData.id,"通过")
+                }
+                //拒绝
+                binding.visitSettings.setOnClickListener {
+                    onItemClickListener.onItemClick(itemData.id,"拒绝")
+                }
+            } catch (e: Exception) {
+                Log.e(" BroadcastListAdapter",e.toString())
+            }
+
+
+        }
+    }
+
+
+    interface OnItemClickListener {
+        fun onItemClick(ID:Int,type: String)
+    }
+    private  var extraParam: String=""
+
+
+    fun updateData(newData: List<VisitPageVo.DataBean>, newExtraParam: String,pageNo:Int) {
+        if(pageNo!=1){
+            data.addAll(newData)
+        }else{
+            data.clear()
+            data.addAll(newData)
+        }
+        extraParam = newExtraParam
+        notifyDataSetChanged()
+    }
+}
+
+

+ 7 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/di/NurseHomeComponent.kt

@@ -2,6 +2,9 @@ package com.wdkl.ncs.android.component.nursehome.di
 
 import com.wdkl.ncs.android.component.nursehome.activity.*
 import com.wdkl.ncs.android.component.nursehome.fragment.*
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitCallFragment
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitMainFragment
+import com.wdkl.ncs.android.component.nursehome.fragment.visit.VisitSetFragment
 import com.wdkl.ncs.android.middleware.di.ApplicationComponent
 import dagger.Component
 
@@ -47,4 +50,8 @@ interface NurseHomeComponent{
     fun inject(activity: SignDataTableFragment)
     fun inject(activity: SignGraphOfCurveFragment)
 
+    fun inject(fragment: VisitMainFragment)
+    fun inject(fragment: VisitCallFragment)
+    fun inject(fragment: VisitSetFragment)
+
 }

+ 2 - 1
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/FramePartFragment.kt

@@ -179,8 +179,9 @@ class FramePartFragment: BaseFragment<FramePartPresenter, FragmentFramePartBindi
      */
     override fun showData(data: FramePartVO) {
         if (data.frameRoomVos != null){
+            //提供给声网探视
+            Constant.union_id =data.framePart.unionId
             val frameRoomVos = data.frameRoomVos as ArrayList<FrameRoomVO>
-
             adapter!!.data.clear()
             adapter!!.data.addAll(frameRoomVos)
             adapter!!.notifyDataSetChanged()

+ 315 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitCallFragment.kt

@@ -0,0 +1,315 @@
+package com.wdkl.ncs.android.component.nursehome.fragment.visit
+
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.SurfaceView
+import android.view.View
+import com.enation.javashop.net.engine.model.NetState
+import com.github.mikephil.charting.data.Entry
+import com.google.common.reflect.TypeToken
+import com.google.gson.FieldNamingPolicy
+import com.google.gson.GsonBuilder
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentVisitAgoreCallBinding
+import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.component.nursehome.visit.tcp.VisitTcpClient
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.BroadcastContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.BroadcastPresenter
+import com.wdkl.ncs.android.middleware.model.dos.BroadcastDO
+import com.wdkl.ncs.android.middleware.model.vo.BedSleepDataVo
+import com.wdkl.ncs.android.middleware.model.vo.VisitPageVo
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.SwVisitUtil
+import io.agora.rtc2.*
+import io.agora.rtc2.video.VideoCanvas
+import kotlinx.android.synthetic.main.fragment_visit_agore_call.*
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.concurrent.TimeUnit
+
+
+class VisitCallFragment : BaseFragment<BroadcastPresenter, FragmentVisitAgoreCallBinding>(), BroadcastContract.View {
+
+    val appId: String = "e1d55db9bcd646f2a23694494526b9d0"
+
+    var TAG = VisitCallFragment::class.java.getSimpleName()
+
+    // 当前用户uid
+    var uuid:Int=0
+    // 频道Token
+    var token:String=""
+    //
+    var channelName:String=""
+    var type :String=""
+    var localAudioMute = false
+    var speakerphone = false
+    var is_bed_user = false
+
+
+    /**
+     * 提供layoutID
+     */
+    override fun getLayId(): Int {
+        return R.layout.fragment_visit_agore_call
+    }
+
+    /**
+     *初始化依赖注入
+     */
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    /**
+     *初始化操作
+     */
+    override fun init() {
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        type = requireArguments().getString("type").toString()
+        channelName = requireArguments().getString("channelName").toString()
+        if (type.equals("3")){
+            visit_voice_call_hangup.visibility =View.GONE
+            visit_call_speaker.visibility =View.GONE
+        }else{
+            visit_voice_call_hangup.visibility =View.VISIBLE
+            visit_call_speaker.visibility =View.VISIBLE
+        }
+//        uuid =((Math.random()*9+1)*100000).toInt().toString()
+        uuid =channelName.toInt()+20
+        if (channelName == ""){
+            showMessage("房间号为空")
+        }else{
+            checkServer()
+        }
+
+    }
+
+
+
+    /**
+     *绑定事件
+     */
+    override fun bindEvent() {
+        //返回
+        visit_voice_call_exit.setOnClickListener {
+            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+        }
+        //喇叭
+        visit_call_speaker.setOnClickListener {
+            speakerphone = !speakerphone
+            mRtcEngine?.muteAllRemoteAudioStreams(speakerphone)
+        }
+        //挂断
+        visit_voice_call_hangup.setOnClickListener {
+         //发送tcp给探视服务器,关闭房间,弹窗提示
+            //通知分机挂断
+            val callTcp = SwVisitUtil.visitHostHangup(Constant.DEVICE_ID,channelName)
+            TcpClient.getInstance().sendMsg(callTcp.toJson())
+            //通知分机挂断
+            val callTcp2 = SwVisitUtil.visitHostHangup2(channelName)
+            VisitTcpClient.getInstance().sendMsg(callTcp2.toJson())
+            //去掉探视中列表
+            NurseHomeActivity.removeValueFromViditingList(channelName)
+            EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+        }
+    }
+
+    /**
+     *页面销毁回调
+     */
+    override fun destory() {
+    }
+    private fun checkServer() {
+        Thread {
+            val okHttpClient = OkHttpClient().newBuilder()
+                    .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .build()
+//            val baseUrl: String ="http://192.168.1.199:7000/ncs_visit_log/get_token"
+            val baseUrl: String ="https://api.base.wdklian.com/ncs_visit_log/get_token"
+            val requestBody = FormBody.Builder()
+                    .add("roomName", channelName)
+                    .add("uid", uuid.toString())
+                    .build()
+
+            val request = Request.Builder()
+                    .url(baseUrl)
+                    .post(requestBody)
+                    .build()
+
+            try {
+                Log.i(TAG, "start check server: $baseUrl")
+                val response = okHttpClient.newCall(request).execute()
+                if (response != null && response.isSuccessful) {
+                    //接口数据获取成功,检查设备是否已注册
+                    val responseBody = response.body()
+                    if (responseBody != null) {
+                        token = responseBody.string()
+                        initView(1,0)
+                    }
+                }
+            } catch (e: Exception) {
+                //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                Log.e(TAG, "check server exception:")
+                e.printStackTrace()
+            }
+        }.start()
+    }
+
+
+    private var mRtcEngine: RtcEngine? = null
+    val handler = Handler(Looper.getMainLooper())
+
+    private fun initView(type: Int ,uid: Int) {
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            // 在这里执行与UI元素相关的操作
+            try {
+                if (type==1){
+                    initializeAndJoinChannel()
+                }else if (type==2){
+                    setupRemoteVideo(uid)
+                }else if (type==3){
+                    setupRemoteVideo2(uid)
+                    visit_call_txt.visibility=View.GONE
+                    visit_voice_call_hangup.visibility=View.VISIBLE
+                    visit_call_speaker.visibility=View.VISIBLE
+                }
+            } catch (e: Exception) {
+
+            }
+        }
+
+    }
+
+
+    private val mRtcEventHandler: IRtcEngineEventHandler = object : IRtcEngineEventHandler() {
+        // 监听频道内的远端用户,获取用户的 uid 信息
+        override fun onUserJoined(uid: Int, elapsed: Int) {
+            if (is_bed_user){
+                initView(2,uid)
+            }else{
+                initView(3,uid)
+            }
+
+        }
+    }
+
+
+    private fun initializeAndJoinChannel() {
+        try {
+            // 创建 RtcEngineConfig 对象,并进行配置
+            val config = RtcEngineConfig()
+            config.mContext = context
+            config.mAppId = appId
+            config.mEventHandler = mRtcEventHandler
+            // 创建并初始化 RtcEngine
+            mRtcEngine = RtcEngine.create(config)
+        } catch (e: Exception) {
+            throw RuntimeException("Check the error.")
+        }
+        // 启用视频模块
+        mRtcEngine?.enableVideo()
+//        // 开启本地预览
+//        mRtcEngine?.startPreview()
+//        val surfaceView = SurfaceView(context)
+//        local_video_view_container.addView(surfaceView)
+//        // 将 SurfaceView 对象传入声网实时互动 SDK,设置本地视图
+//        mRtcEngine?.setupLocalVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0))
+        // 创建 ChannelMediaOptions 对象,并进行配置
+        val options = ChannelMediaOptions()
+        // 根据场景将用户角色设置为 BROADCASTER (主播) 或 AUDIENCE (观众)
+        options.clientRoleType = Constants.CLIENT_ROLE_AUDIENCE
+        // 直播场景下,设置频道场景为 BROADCASTING (直播场景)
+        options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
+        // 使用临时 Token 加入频道,自行指定用户 ID 并确保其在频道内的唯一性
+        mRtcEngine?.joinChannel(token, channelName, uuid.toInt(), options)
+
+    }
+
+    private fun setupRemoteVideo(uid: Int) {
+
+        val surfaceView = SurfaceView(context)
+        surfaceView.setZOrderMediaOverlay(true)
+        remote_video_view_container.addView(surfaceView)
+        // 将 SurfaceView 对象传入声网实时互动 SDK,设置远端视图
+        mRtcEngine!!.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))
+    }
+
+    private fun setupRemoteVideo2(uid: Int) {
+        is_bed_user =true
+        val surfaceView = SurfaceView(context)
+        surfaceView.setZOrderMediaOverlay(true)
+        local_video_view_user.addView(surfaceView)
+        // 将 SurfaceView 对象传入声网实时互动 SDK,设置远端视图
+        mRtcEngine!!.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))
+    }
+    /**
+     *显示数据
+     */
+    override fun showData(data: ArrayList<BroadcastDO>) {
+    }
+
+    /**
+     *处理错误信息
+     */
+    override fun onError(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载完成
+     */
+    override fun complete(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载开始
+     */
+    override fun start() {
+    }
+
+    /**
+     *处理网络状态
+     */
+    override fun networkMonitor(state: NetState) {
+    }
+    override fun onStart() {
+        super.onStart()
+        if (!EventBus.getDefault().isRegistered(this)) {
+            // 未注册的逻辑处理
+            EventBus.getDefault().register(this)
+        }
+
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (EventBus.getDefault().isRegistered(this)) {
+            // 已注册的逻辑处理
+            EventBus.getDefault().unregister(this)
+        }
+        //停止本地视频预览
+        mRtcEngine?.stopPreview();
+        //离开频道
+        mRtcEngine?.leaveChannel();
+        is_bed_user =false
+
+    }
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+
+    }
+
+
+}

+ 389 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitMainFragment.kt

@@ -0,0 +1,389 @@
+package com.wdkl.ncs.android.component.nursehome.fragment.visit
+
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.google.common.reflect.TypeToken
+import com.google.gson.FieldNamingPolicy
+import com.google.gson.GsonBuilder
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
+import com.wdkl.ncs.android.component.nursehome.adapter.VisitListAdapter
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentVisitAgoreMainBinding
+import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.BroadcastContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.BroadcastPresenter
+import com.wdkl.ncs.android.middleware.model.dos.BroadcastDO
+import com.wdkl.ncs.android.middleware.model.vo.VisitPageVo
+import kotlinx.android.synthetic.main.fragment_visit_agore_main.*
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.concurrent.TimeUnit
+
+
+class VisitMainFragment : BaseFragment<BroadcastPresenter, FragmentVisitAgoreMainBinding>(), BroadcastContract.View, VisitListAdapter.OnItemClickListener {
+
+
+    var TAG = VisitMainFragment::class.java.getSimpleName()
+
+
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    var visitListAdapter: VisitListAdapter? = null
+    var pageNo = 1
+    var status:String=""
+    var  shopUnionId:String=""
+
+    private var data = ArrayList<VisitPageVo.DataBean>()
+    val newData = ArrayList<VisitPageVo.DataBean>()
+    private lateinit var responsedata : VisitPageVo
+    /**
+     * 提供layoutID
+     */
+    override fun getLayId(): Int {
+        return R.layout.fragment_visit_agore_main
+    }
+
+    /**
+     *初始化依赖注入
+     */
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    /**
+     *初始化操作
+     */
+    override fun init() {
+          setui(1)
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+
+        visitListAdapter = VisitListAdapter(ArrayList())
+        visitListAdapter!!.setOnItemClickListener(this)
+        delegateAdapter.addAdapter(visitListAdapter)
+
+        /**配置到RecycleView*/
+        visit_main_br_listView.layoutManager = virtualLayoutManager
+        visit_main_br_listView.adapter = delegateAdapter
+          checkServer()
+    }
+    private fun checkServer() {
+        Thread {
+            val okHttpClient = OkHttpClient().newBuilder()
+                    .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .build()
+//            val baseUrl: String ="http://192.168.1.199:7000/ncs_visit_log/get_page"
+            shopUnionId = Constant.union_id
+            if (shopUnionId.equals("")) {
+                showMessage("机构id为空")
+                     initView(2)
+                 return@Thread
+            }
+
+
+//                val url = "$baseUrl?sn=$sn&reportTime=$reportTime"
+            val baseUrl: String ="https://api.base.wdklian.com/ncs_visit_log/get_page"
+
+            val requestBody = FormBody.Builder()
+                    .add("pageNo", pageNo.toString())
+                    .add("pageSize", "5")
+//                    .add("shopUnionId", "610a4ebae21b840007887b95")
+                    .add("shopUnionId", shopUnionId)
+                    .add("memberId", "")
+                    .add("status", status)
+                    .build()
+
+            val request = Request.Builder()
+                    .url(baseUrl)
+                    .post(requestBody)
+                    .build()
+
+            try {
+                Log.i(TAG, "start check server: $baseUrl")
+                val response = okHttpClient.newCall(request).execute()
+                if (response != null && response.isSuccessful) {
+                    //接口数据获取成功,检查设备是否已注册
+                    val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                    val itemType = object : TypeToken<VisitPageVo>(){}.type
+                    responsedata= gson.fromJson(response.body()?.string(), itemType)
+                    data = responsedata.data as ArrayList<VisitPageVo.DataBean>
+//                    data = gson.fromJson<ArrayList<VisitPageVo.DataBean>>(response.body()?.string(), itemType)
+//                    Log.e(TAG, "check server exception:" + allOrders.toString())
+                    initView(1)
+                } else {
+                    //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                    initView(2)
+                }
+            } catch (e: Exception) {
+                initView(2)
+                //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                Log.e(TAG, "check server exception:")
+                e.printStackTrace()
+            }
+        }.start()
+    }
+
+    private fun initView(type: Int) {
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            // 在这里执行与UI元素相关的操作
+            try {
+                if (type==1){
+                    visit_main_refresh.finishRefresh()
+                    visit_main_refresh.finishLoadMore()
+                    if (data!=null && data.size>0){
+                        // 如果 viditingList 为空,则直接刷新为空,并更新 RecyclerView
+
+
+                        if (status.equals("1")) {
+                                    if (NurseHomeActivity.viditingList.isEmpty()) {
+                                        visit_main_emptyImageView.visibility= View.VISIBLE
+                                        data.clear()
+                                        // 刷新 RecyclerView
+                                        visitListAdapter!!.updateData(data,status,pageNo)
+                                        return@post
+                                    }else{
+                                        newData.clear()
+                                        for (itemData in data) {
+                                            if (NurseHomeActivity.viditingList.contains(itemData.id.toString())){
+                                                newData.add(itemData)
+                                            }
+                                        }
+                                        // 将新列表的内容复制回原始列表
+                                        data.clear()
+                                        data.addAll(newData)
+                                    }
+                         }
+                        visit_main_emptyImageView.visibility= View.GONE
+                        //如果是探视中的
+                        visitListAdapter!!.updateData(data,status,pageNo)
+//                        if (pageNo!=1){
+//                            visitListAdapter!!.data.addAll(data)
+//                        }else{
+//                            visitListAdapter!!.data.clear()
+//                            visitListAdapter!!.data.addAll(data)
+//                        }
+//                        //Log.i(TAG,"其它主机数量 " + devices.size)
+//                        visitListAdapter!!.notifyDataSetChanged()
+                    }else{
+
+                        if (pageNo>1){
+                            showMessage("没有更多数据")
+                            return@post
+                        }
+                        visit_main_emptyImageView.visibility= View.VISIBLE
+                    }
+                }else{
+                    visit_main_refresh.finishRefresh()
+                    visit_main_refresh.finishLoadMore()
+                    visit_main_emptyImageView.visibility= View.VISIBLE
+                }
+            } catch (e: Exception) {
+                initView(2)
+            }
+        }
+
+    }
+    /**
+     *绑定事件
+     */
+    override fun bindEvent() {
+
+        visit_main_refresh.setOnRefreshListener {
+            if (!Constant.union_id.equals("")) {
+                pageNo = 1
+                checkServer()
+            } else {
+                showMessage("no union id")
+            }
+        }
+
+        visit_main_refresh.setOnLoadMoreListener {
+            if (!Constant.union_id.equals("")) {
+                pageNo += 1
+                checkServer()
+            } else {
+                showMessage("no union id")
+            }
+        }
+        //预约列表
+        visit_main_list_bt.setOnClickListener {
+            setui(1)
+
+        }
+        //喊话广播
+        visit_main_sk_bt.setOnClickListener {
+            setui(2)
+
+        }
+        //更多功能
+        visit_main_all_bt.setOnClickListener {
+            setui(3)
+        }
+
+    }
+
+    private fun  setui(int: Int){
+        visit_main_list_bt.setBackgroundResource(R.drawable.shape_bed_bg)
+        visit_main_list_bt_img.setBackgroundResource(R.mipmap.jr)
+        visit_main_list_img.setBackgroundResource(R.mipmap.visit_list_img)
+        visit_main_list_bt_tx.setTextColor(getResources().getColor(R.color.text_name_color))
+
+        visit_main_sk_bt.setBackgroundResource(R.drawable.shape_bed_bg)
+        visit_main_sk_bt_img.setBackgroundResource(R.mipmap.jr)
+        visit_main_sk_img.setBackgroundResource(R.mipmap.visit_sk_img)
+        visit_main_sk_bt_tx.setTextColor(getResources().getColor(R.color.black))
+
+        visit_main_all_bt.setBackgroundResource(R.drawable.shape_bed_bg)
+        visit_main_all_bt_img.setBackgroundResource(R.mipmap.jr)
+        visit_main_all_img.setBackgroundResource(R.mipmap.visit_all_img)
+        visit_main_all_bt_tx.setTextColor(getResources().getColor(R.color.black))
+        when (int) {
+
+            1-> {
+                status="1"
+                pageNo = 1
+                checkServer()
+                //预约列表
+                visit_main_list_bt.setBackgroundResource(R.drawable.shape_bt_main_list_bg)
+                visit_main_list_img.setBackgroundResource(R.mipmap.visit_list_img_w)
+                visit_main_list_bt_img.setBackgroundResource(R.mipmap.jian_bai)
+                visit_main_list_bt_tx.setTextColor(getResources().getColor(R.color.white));
+            }
+
+            2 -> {
+                status="0"
+                pageNo = 1
+                checkServer()
+                //审核列表
+                visit_main_sk_bt.setBackgroundResource(R.drawable.shape_bt_main_hh_bg)
+                visit_main_sk_img.setBackgroundResource(R.mipmap.visit_sk_img_w)
+                visit_main_sk_bt_img.setBackgroundResource(R.mipmap.jian_bai)
+
+                visit_main_sk_bt_tx.setTextColor(getResources().getColor(R.color.white))
+            }
+            3 -> {
+                status=""
+                pageNo = 1
+                checkServer()
+                //全部
+                visit_main_all_bt.setBackgroundResource(R.drawable.shape_bt_main_add_bg)
+                visit_main_all_img.setBackgroundResource(R.mipmap.visit_all_img_w)
+                visit_main_all_bt_img.setBackgroundResource(R.mipmap.jian_bai)
+
+                visit_main_all_bt_tx.setTextColor(getResources().getColor(R.color.white))
+            }
+        }
+    }
+
+    /**
+     *页面销毁回调
+     */
+    override fun destory() {
+    }
+
+    /**
+     *显示数据
+     */
+    override fun showData(data: ArrayList<BroadcastDO>) {
+    }
+
+    /**
+     *处理错误信息
+     */
+    override fun onError(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载完成
+     */
+    override fun complete(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载开始
+     */
+    override fun start() {
+    }
+
+    /**
+     *处理网络状态
+     */
+    override fun networkMonitor(state: NetState) {
+    }
+    override fun onStart() {
+        super.onStart()
+        if (!EventBus.getDefault().isRegistered(this)) {
+            // 未注册的逻辑处理
+            EventBus.getDefault().register(this)
+        }
+
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (EventBus.getDefault().isRegistered(this)) {
+            // 已注册的逻辑处理
+            EventBus.getDefault().unregister(this)
+        }
+
+    }
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+//        Log.e(TAG,"收到tcp消息")
+        if (messageEvent.type ==Constant.EVENT_VISIT_list){
+            if (messageEvent.message.equals("BackCall") && status.equals("1")){
+                setui(1)
+                showMessage("收到新的审核信息,已刷新")
+                return
+            }
+            if (messageEvent.message.equals("refresh") && status.equals("2")){
+                setui(2)
+                return
+            }
+        }else if (messageEvent.type ==Constant.EVENT_REMOVE_CALL_FRAGMENT && status.equals("1")){
+            setui(1)
+        }
+    }
+
+    override fun onItemClick(ID: Int,  type: String) {
+        when (type){
+            "查看" -> {
+                if (status.equals("")){
+                    EventBus.getDefault().post(MessageEvent("3", Constant.EVENT_VISIT_START,ID.toString()))
+                }else{
+                    EventBus.getDefault().post(MessageEvent(status, Constant.EVENT_VISIT_START,ID.toString()))
+                }
+
+            }
+            "停止" -> {
+
+            }
+            "通过" -> {
+                EventBus.getDefault().post(MessageEvent(ID.toString(), Constant.EVENT_V_VISIT_th,"通过"))
+            }
+            "拒绝" -> {
+                EventBus.getDefault().post(MessageEvent(ID.toString(), Constant.EVENT_V_VISIT_th,"拒绝"))
+            }
+
+        }
+    }
+}

+ 371 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/visit/VisitSetFragment.kt

@@ -0,0 +1,371 @@
+package com.wdkl.ncs.android.component.nursehome.fragment.visit
+
+import android.graphics.Color
+import android.os.Handler
+import android.os.Looper
+import android.text.Editable
+import android.text.TextUtils
+import android.text.TextWatcher
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.bigkoo.pickerview.builder.TimePickerBuilder
+import com.enation.javashop.net.engine.model.NetState
+import com.github.mikephil.charting.data.Entry
+import com.google.common.reflect.TypeToken
+import com.google.gson.FieldNamingPolicy
+import com.google.gson.GsonBuilder
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.adapter.BrGroupAdapter
+import com.wdkl.ncs.android.component.nursehome.adapter.VisitGroupAdapter
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentBrSetBinding
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentVisitThroughBinding
+import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.BroadcastSetContract
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.FramePartContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.BroadcastSetPresenter
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.FramePartPresenter
+import com.wdkl.ncs.android.middleware.model.dos.RemarkDO
+import com.wdkl.ncs.android.middleware.model.vo.*
+import kotlinx.android.synthetic.main.fragment_br_set.*
+import kotlinx.android.synthetic.main.fragment_br_set.set_br_ed_name
+import kotlinx.android.synthetic.main.fragment_visit_agore_main.*
+import kotlinx.android.synthetic.main.fragment_visit_through.*
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.*
+import java.util.concurrent.TimeUnit
+import kotlin.collections.ArrayList
+
+/**
+ * 探视审核设置界面
+ */
+class VisitSetFragment : BaseFragment<FramePartPresenter, FragmentVisitThroughBinding>(), FramePartContract.View ,VisitGroupAdapter.OnItemClickListener{
+
+
+    var TAG = VisitSetFragment::class.java.getSimpleName()
+
+    //床位适配器
+    private lateinit var adapter: VisitGroupAdapter
+    private var infoFragment: Fragment? = null
+
+    var  group_id :Int = 0
+    private var allOrders = ArrayList<FrameBedVO>()
+
+
+
+    //设备id
+    var  groupIds :String=""
+    //拒绝理由
+    var  title :String=""
+    //拒绝理由
+    var  type :String=""
+    //探视id
+    var  visit_id :String=""
+    // 状态 0审核中、1审核通过、2审核不通过、3完成
+    var  status :String=""
+
+
+
+    /**
+     * 提供layoutID
+     */
+    override fun getLayId(): Int {
+        return R.layout.fragment_visit_through
+    }
+
+    /**
+     *初始化依赖注入
+     */
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    /**
+     *初始化操作
+     */
+    override fun init() {
+        type = requireArguments().getString("channelName").toString()
+        visit_id = requireArguments().getString("id").toString()
+
+        if (type.equals("通过") && !visit_id.equals("")){
+            visit_through_ll_1.visibility = View.GONE
+            visit_through_ll_2.visibility = View.VISIBLE
+            status="1"
+            adapter = VisitGroupAdapter(activity, ArrayList())
+            val layoutManager = GridLayoutManager(activity, 4, LinearLayoutManager.VERTICAL, false)
+            visit_through_info.layoutManager = layoutManager
+            adapter.setOnItemClickListener(this)
+            visit_through_info.adapter = adapter
+            if (Constant.PART_ID!=null && Constant.PART_ID!=1 ){
+                presenter.loadData(Constant.PART_ID)
+            }
+        }else if (type.equals("拒绝") && !visit_id.equals("")){
+            visit_through_ll_1.visibility = View.VISIBLE
+            visit_through_ll_2.visibility = View.GONE
+            status="2"
+        }else{
+            EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_r_VISIT_th))
+            showMessage("数据不全")
+            return
+        }
+
+    }
+
+    /**
+     *绑定事件
+     */
+    override fun bindEvent() {
+        setupTextViewClickListeners(mViewDataBinding)
+        //返回
+        visit_through_retun_bt.setOnClickListener {
+            EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_r_VISIT_th))
+        }
+        //修改
+        visit_through_save_bt.setOnClickListener {
+                //标题
+            if (! visit_through_ed_name.text.equals("") ){
+                title =visit_through_ed_name.text.toString()
+            }
+            if(type.equals("通过") &&groupIds.equals("")){
+                showMessage("请选择床位")
+                return@setOnClickListener
+            }
+            if (type.equals("拒绝") && title.equals("")){
+                showMessage("请选择拒绝理由")
+                return@setOnClickListener
+            }
+            checkServer()
+        }
+
+    }
+    private fun setupTextViewClickListeners(binding: FragmentVisitThroughBinding) {
+        val textViewList = listOf<TextView>(
+                binding.visitThroughTx3,
+                binding.visitThroughTx4,
+                binding.visitThroughTx5,
+                binding.visitThroughTx6,
+                binding.visitThroughTx7,
+                binding.visitThroughTx8,
+                binding.visitThroughTx9
+        )
+
+
+        var selectedTextView: TextView? = null // 用于跟踪当前选中的TextView
+        for (textView in textViewList) {
+            textView.setOnClickListener {
+                // 取消之前选中的TextView的状态
+                selectedTextView?.let {
+                    it.setBackgroundResource(R.drawable.shape_time_f_bg)
+                    it.setTextColor(Color.parseColor("#B4B4B4"))
+                }
+
+                // 更新选中的TextView
+                selectedTextView = textView
+                selectedTextView?.let {
+                    it.setBackgroundResource(R.drawable.shape_time_f_bg_d)
+                    it.setTextColor(Color.parseColor("#0090ff"))
+                    title = it.text.toString()
+                    visit_through_ed_name.setText(title)
+                }
+            }
+        }
+//        val clickedTextViews = mutableListOf<Int>()
+//        for (i in 0 until textViewList.size) {
+//            val textView = textViewList[i]
+//            textView.setOnClickListener {
+//                val clickedIndex = clickedTextViews.indexOf(i)
+//                if (clickedIndex >= 0) {
+//                    clickedTextViews.removeAt(clickedIndex)
+//                    textView.setBackgroundResource(R.drawable.shape_time_f_bg)
+//                    textView.setTextColor(Color.parseColor("#B4B4B4"))
+//                } else {
+//                    clickedTextViews.add(i)
+//                    textView.setBackgroundResource(R.drawable.shape_time_f_bg_d)
+//                    textView.setTextColor(Color.parseColor("#0090ff"))
+//                    title =textView.text.toString()
+//                    visit_through_ed_name.setText(title)
+//                }
+//            }
+//        }
+    }
+
+    override fun onItemClick(view: View, keyId: Int, ids: String) {
+        groupIds = ids
+    }
+    /**
+     *页面销毁回调
+     */
+    override fun destory() {
+    }
+
+    override fun showData(data: FramePartVO) {
+        if (data.frameRoomVos != null){
+         val frameRoomVos = data.frameRoomVos as ArrayList<FrameRoomVO>
+            val frameBedVO = ArrayList<FrameBedVO>()
+            val emptyFrameBedVOs = ArrayList<FrameBedVO>()
+            for (frameRoomVo in frameRoomVos) {
+                val frameBedVOs = frameRoomVo.frameBedList
+
+                if (frameBedVOs != null && frameBedVOs.size > 0) {
+                    for (bedVO in frameBedVOs) {
+                        if (TextUtils.isEmpty(bedVO.customerName) || bedVO.bedDeviceId==null ) {
+                            emptyFrameBedVOs.add(bedVO)
+                        } else {
+                            frameBedVO.add(bedVO)
+                        }
+                    }
+                }
+            }
+            allOrders.clear()
+            allOrders.addAll(frameBedVO)
+            adapter.updateData(allOrders)
+
+        }
+    }
+    private fun checkServer() {
+        Thread {
+            val okHttpClient = OkHttpClient().newBuilder()
+                    .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
+                    .build()
+//            val baseUrl: String ="http://192.168.1.199:7000/ncs_visit_log/edit/"+visit_id
+//                val url = "$baseUrl?sn=$sn&reportTime=$reportTime"
+            val baseUrl: String ="https://api.base.wdklian.com/ncs_visit_log/edit"+visit_id
+
+
+            val requestBody = FormBody.Builder()
+//                    .add("id", visit_id)
+                    .add("callBedId", groupIds)
+                    .add("status", status)
+                    .add("refuseReason", title)
+
+                    .build()
+
+            Log.e(TAG,"预约ID"+visit_id+"床位id"+groupIds+"审核状态"+status+"拒绝理由"+title)
+
+            val request = Request.Builder()
+                    .url(baseUrl)
+                    .post(requestBody)
+                    .build()
+
+            try {
+                Log.i(TAG, "start check server: $baseUrl")
+                val response = okHttpClient.newCall(request).execute()
+                if (response != null && response.isSuccessful) {
+                    initView(1)
+                } else {
+                    //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                    initView(2)
+                }
+            } catch (e: Exception) {
+
+                //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                Log.e(TAG, "check server exception:")
+                e.printStackTrace()
+            }
+        }.start()
+    }
+
+
+    private fun initView(type: Int) {
+        val handler = Handler(Looper.getMainLooper())
+        handler.post {
+            // 在这里执行与UI元素相关的操作
+            try {
+                if (type==1){
+                    showMessage("审核成功")
+                    EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_r_VISIT_th))
+                }else{
+                    showMessage("审核失败")
+                    EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_r_VISIT_th))
+                }
+            } catch (e: Exception) {
+            }
+        }
+
+    }
+    override fun showCustomerInfo(data: CustomerInfoVO) {
+
+    }
+
+    override fun showPersonMemoAndRemarkData(data: ArrayList<RemarkDO>) {
+
+    }
+
+    override fun showFamilyMembersData(data: FamilyMembersVO) {
+
+    }
+
+    override fun showTaskData(data: TaskVO) {
+
+    }
+
+    override fun onNoNet() {
+
+    }
+
+
+    /**
+     *处理错误信息
+     */
+    override fun onError(message: String, type: Int) {
+        showMessage(message)
+    }
+
+    /**
+     *耗时加载完成
+     */
+    override fun complete(message: String, type: Int) {
+    }
+
+    /**
+     *耗时加载开始
+     */
+    override fun start() {
+    }
+
+    /**
+     *处理网络状态
+     */
+    override fun networkMonitor(state: NetState) {
+    } override fun onStart() {
+        super.onStart()
+        if (!EventBus.getDefault().isRegistered(this)) {
+            // 未注册的逻辑处理
+            EventBus.getDefault().register(this)
+        }
+
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (EventBus.getDefault().isRegistered(this)) {
+            // 已注册的逻辑处理
+            EventBus.getDefault().unregister(this)
+        }
+
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+
+    }
+
+
+}

+ 156 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/util/BluetoothUtil.java

@@ -0,0 +1,156 @@
+package com.wdkl.ncs.android.component.nursehome.util;
+
+public class BluetoothUtil {
+
+
+    /**
+     * Hex转byte[],两种情况,Hex长度为奇数最后一个字符会被舍去
+     */
+    public static byte[] hexTobytes(String hex) {
+        if (hex.length() < 1) {
+            return null;
+        } else {
+            byte[] result = new byte[hex.length() / 2];
+            int j = 0;
+            for(int i = 0; i < hex.length(); i+=2) {
+                result[j++] = (byte) Integer.parseInt(hex.substring(i,i+2), 16);
+            }
+            return result;
+        }
+    }
+
+    // Helper method to extract bytes from byte array.
+    public static byte[] extractBytes(byte[] scanRecord, int start, int length) {
+        byte[] bytes = new byte[length];
+        System.arraycopy(scanRecord, start, bytes, 0, length);
+        return bytes;
+    }
+
+    // 格式化UUID
+    public static String parseUUID(String data) {
+        String uuid = "";
+        if (data.length() == 32) {
+            uuid = data.substring(0, 8) + "-"
+                    + data.substring(8, 12) + "-"
+                    + data.substring(12, 16) + "-"
+                    + data.substring(16, 20) + "-"
+                    + data.substring(20);
+        } else {
+//            showTips(getString(R.string.toast_uuid_not_found));
+        }
+        return uuid;
+    }
+
+    /**
+     * byte数组转化为string
+     */
+    static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+    public static String bytesToHex(byte[] bytes) {
+        char[] hexChars = new char[bytes.length * 2];
+        for (int j = 0; j < bytes.length; j++) {
+            int v = bytes[j] & 0xFF;
+            hexChars[j * 2] = hexArray[v >>> 4];
+            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+        }
+        return new String(hexChars);
+    }
+
+    // 根据RSSI计算距离
+    public static double rssi2distance(int rssi) {
+        int iRssi = Math.abs(rssi);
+        // 发射端和接收端相隔1米时的信号强度
+        int A = 59;
+        // 环境噪声衰减因子
+        double n = 2.0;
+        double power = (iRssi - A) / (10 * n);
+        return Math.pow(10, power);
+    }
+
+    public static byte[] hexStringToByteArray(String hexString) {
+        hexString = hexString.replaceAll(" ", "");
+        int len = hexString.length();
+        byte[] bytes = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
+            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
+                    .digit(hexString.charAt(i + 1), 16));
+        }
+        return bytes;
+    }
+
+    /**
+     * 16进制转换成为string类型字符串
+     * @param s
+     * @return
+     */
+    public static String hexStringToString(String s) {
+        if (s == null || s.equals("")) {
+            return null;
+        }
+        s = s.replace(" ", "");
+        byte[] baKeyword = new byte[s.length() / 2];
+        for (int i = 0; i < baKeyword.length; i++) {
+            try {
+                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        try {
+            s = new String(baKeyword, "UTF-8");
+            new String();
+        } catch (Exception e1) {
+            e1.printStackTrace();
+        }
+        return s;
+    }
+
+    /**
+     * 字符串转化成为16进制字符串
+     * @param s
+     * @return
+     */
+    public static String strTo16(String s) {
+        String str = "";
+        for (int i = 0; i < s.length(); i++) {
+            int ch = (int) s.charAt(i);
+            String s4 = Integer.toHexString(ch);
+            str = str + s4;
+        }
+        return str.toUpperCase();
+    }
+
+    public static String intToTwoBytesHexString(int number) {
+        // 将整数转换为两个字节的十六进制表示
+        byte[] bytes = new byte[2];
+        bytes[0] = (byte) ((number >> 8) & 0xFF);  // 高位字节
+        bytes[1] = (byte) (number & 0xFF);         // 低位字节
+
+        // 将字节数组转换为十六进制字符串
+        StringBuilder hexString = new StringBuilder();
+        for (byte b : bytes) {
+            hexString.append(String.format("%02X", b));
+        }
+        return hexString.toString();
+    }
+
+    /**
+     * 10进制转化成为16进制字符串
+     * @return
+     */
+    public static String hex10To16(String valueTen) {
+        return String.format("%04X",valueTen);//自定义保留几位
+    }
+    /**
+     * 16进制字符串转化成为10进制
+     * @return
+     */
+    public static int hexToDecimal(String hexString) {
+        try {
+            return Integer.parseInt(hexString, 16);
+        } catch (NumberFormatException e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}

+ 143 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/util/NotificationUtil.java

@@ -0,0 +1,143 @@
+package com.wdkl.ncs.android.component.nursehome.util;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+
+import com.enation.javashop.android.welcome.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import androidx.annotation.RequiresApi;
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+
+/**
+ * 通知栏工具
+ */
+public class NotificationUtil {
+
+    private String TAG =NotificationUtil.class.getSimpleName();
+    private Context mContext;
+    private NotificationManager notificationManager;
+    private int progress = 0;
+    public int progressMax = 600;
+    public int notificationId = 0x2154;
+    private Notification.Builder builder;
+    private String ContentTitle = "care service";
+    private String ContentText = "running...";
+    String channelId = "my_channel_01";
+    String name = "my_channel";
+
+    private SimpleDateFormat sdfInput = new SimpleDateFormat("mm:ss ");
+
+    /**
+     * @param context
+     */
+    public NotificationUtil(Context context) {
+        this.mContext = context;
+        notificationManager = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
+        sendNotification();
+    }
+
+    public Notification.Builder getBuilder(){
+        return builder;
+    }
+
+    public void setProgres(int progress){
+        this.progress = progress;
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private void sendNotification() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            @SuppressLint("WrongConstant")
+            NotificationChannel mChannel = new NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_LOW);
+            notificationManager.createNotificationChannel(mChannel);
+            builder = new Notification.Builder(mContext)
+                    .setChannelId(channelId)  //设置渠道id
+                    .setContentTitle(ContentTitle)
+                    .setContentText(ContentText)
+                    .setSmallIcon(R.mipmap.launcher);
+
+
+            //创建点击跳转Intent 不会重新新建界面
+            /*Intent intent = new Intent(mContext, MainActivity.class);
+            //创建任务栈Builder
+            TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
+            stackBuilder.addParentStack(MainActivity.class);
+            stackBuilder.addNextIntent(intent);
+            PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
+            builder.setContentIntent(pendingIntent);*/
+        } else {
+            builder = new Notification.Builder(mContext)
+                    .setContentTitle(ContentTitle)
+                    .setContentText(ContentText)
+                    .setSmallIcon(R.mipmap.launcher)
+                    .setOngoing(true);
+//                    .setChannelId(channelId);//无效
+        }
+        notificationManager.notify(notificationId, builder.build());
+    }
+
+
+    /**
+     * @param contentText
+     * @return
+     */
+    public NotificationUtil setContentText(String contentText) {
+        ContentText = contentText;
+        return NotificationUtil.this;
+    }
+
+
+    /**
+     * @param contentTitle
+     * @return
+     */
+    public NotificationUtil setContentTitle(String contentTitle) {
+        ContentTitle = contentTitle;
+        return NotificationUtil.this;
+    }
+
+
+    /**
+     * @param progress
+     */
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+    public void setProgress(int progress) {
+        if (builder == null) return;
+        builder.setProgress(progressMax, progress, false);
+        builder.setContentTitle(sdfInput.format(new Date(progress*1000)));
+        if (progress == progressMax) {
+            builder.setContentTitle("download complete");
+            //自动移除
+            notificationManager.cancel(notificationId);
+        } else {
+            notificationManager.notify(notificationId, builder.build());//显示通知
+        }
+    }
+
+
+    public void ceShi() {
+        new Thread(new Runnable() {
+            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
+            @Override
+            public void run() {
+                while (progress++ < progressMax) {
+                    setProgress(progress);
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }).start();
+    }
+}

+ 146 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitService.java

@@ -0,0 +1,146 @@
+package com.wdkl.ncs.android.component.nursehome.visit;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.Log;
+import android.util.SparseArray;
+
+
+import com.wdkl.ncs.android.component.nursehome.util.BluetoothUtil;
+import com.wdkl.ncs.android.component.nursehome.util.NotificationUtil;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+
+
+import androidx.annotation.RequiresApi;
+import serialporttest.utils.SerialPortUtilSleep;
+
+
+public class VisitService extends Service implements VisitSocketConnect.OnConnectStateListener, VisitSocketConnect.OnSocketReadListener {
+
+    private static String TAG = VisitService.class.getSimpleName();
+    private VisitSocketConnectLine VisitSocketConnectLine;
+    private String ip = "47.115.176.250";
+    private int port = 3006;
+    private static VisitService visitService;
+    public static boolean VisitconnectState = false;
+
+    NotificationUtil notificationUtil;
+
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new ServiceBinder();
+    }
+
+
+
+
+    public class ServiceBinder extends Binder {
+        public VisitService getService() {
+            return VisitService.this;
+        }
+
+        public void setData(String data) {
+            sendData(data);
+        }
+
+//        public void setOnSignDataCallback(SleepDataParse.OnDataCallback callback) {
+//            dataCallback = callback;
+//        }
+    }
+
+
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        EventBus.getDefault().register(this);
+        //启动通知栏
+        notificationUtil = new NotificationUtil(this);
+        startForeground(notificationUtil.notificationId, notificationUtil.getBuilder().build());
+
+        visitService = this;
+        ip = Constant.SLEEP_TCP_SERVER_URL;
+        port = Constant.SLEEP_TCP_PORT;
+        Log.e(TAG, "ip " + ip + " port " + port);
+        VisitSocketConnectLine = new VisitSocketConnectLine();
+        VisitSocketConnectLine.setIp(ip);
+        VisitSocketConnectLine.setPort(port);
+ //     VisitSocketConnectLine.setHeartbeatTime(1000 * 10);
+//        String data = String.format("0-%s:%s:%s",  NetFunctionConfig.getUser() + "", BaxcWebConfig.MACHINE_TYPE, Util.getDeviceId(WebSocketClinetService2.this)); //机构ID:机器类型:机器码
+        final String data = "0";
+        //ip = "47.115.176.250";
+        //port = 3006;
+        VisitSocketConnectLine.setHeartbeat(BluetoothUtil.hexStringToByteArray(data));//发送心跳数据
+        if (!VisitconnectState  &&  Constant.CUSTOM_ID != -1){
+            VisitSocketConnectLine.start();
+            VisitSocketConnectLine.setOnConnectStateListener(this);//监听是否在线
+            VisitSocketConnectLine.setOnSocketReadListener(this);  //监听返回数据
+        }
+
+
+    }
+
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.e(TAG, "onStartCommand....");
+
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+
+    @Override
+    public void onSocketRead(VisitSocketConnect install, byte[] bytes) {
+
+    }
+
+    @Override
+    public void onConnectState(VisitSocketConnect install, boolean connectState) {
+
+    }
+
+
+    /**
+     * 发送数据
+     *
+     * @param data
+     */
+    public void sendData(final String data) {
+        if (VisitSocketConnectLine != null) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    Log.e(TAG, "发送的数据 " + data);
+                    VisitSocketConnectLine.send(BluetoothUtil.hexStringToByteArray(data));
+                }
+            }).start();
+
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.e(TAG, "床垫服务关闭");
+        SerialPortUtilSleep.getInstance().closeSerialPort();
+        EventBus.getDefault().unregister(this);
+        visitService = null;
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.ASYNC)
+    public void onMoonEvent(MessageEvent messageEvent) {
+
+    }
+}

+ 80 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitSocketConnect.java

@@ -0,0 +1,80 @@
+package com.wdkl.ncs.android.component.nursehome.visit;
+
+
+/**
+ * socket连接类 断开连接可自动重连
+ * author 许柏胜
+ */
+public interface VisitSocketConnect {
+    int ERROR_START_FAIL = -1;
+    int ERROR_RELEASE_FAIL = -1;
+    int ERROR_SEND_NOT_CONNECT = -1; //未连接
+    int ERROR_SEND_IO_ERROR = -2; //IO异常
+
+    /**
+     * 开始连接
+     * @return 0为成功 非0为失败
+     */
+    int start();
+
+    /**
+     * 释放资源
+     * @return 0为成功 非0为失败
+     */
+    int release();
+
+    /**
+     * 发送数据
+     * @param datas
+     * @return 小于0为失败
+     */
+    int send(byte[] datas);
+
+    boolean isConnect();
+
+    void setIp(String ip);
+    void setPort(int port);
+    void setTag(String tag);
+
+    /**
+     * 设置心跳
+     * @param heartbeat
+     */
+    void setHeartbeat(byte[] heartbeat);
+
+    /**
+     * 设置心跳间隔时间
+     * @param heartbeatTime 默认为5秒
+     */
+    void setHeartbeatTime(long heartbeatTime);
+
+    long getHeartbeatTime();
+    byte[] getHeartbeat();
+    String getIp();
+    int getPort();
+    String getTag();
+
+
+    void setOnSocketReadListener(OnSocketReadListener onSocketReadListener);
+
+    void setOnConnectStateListener(OnConnectStateListener onConnectStateListener);
+
+    /**
+     * 读取回调
+     */
+    interface OnSocketReadListener{
+        void onSocketRead(VisitSocketConnect install, byte[] bytes);
+    }
+
+    /**
+     * 连接状态监听
+     */
+    interface OnConnectStateListener{
+        /**
+         *
+         * @param connectState true为连接成功 false为断开连接
+         */
+        void onConnectState(VisitSocketConnect install, boolean connectState);
+    }
+
+}

+ 384 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/VisitSocketConnectLine.java

@@ -0,0 +1,384 @@
+package com.wdkl.ncs.android.component.nursehome.visit;
+
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * socket客户端 读取一行一行数据
+ * author 许柏胜
+ */
+public class VisitSocketConnectLine implements VisitSocketConnect {
+
+    private String ip;
+    private int port;
+    private String tag;
+    private byte[] heartbeat;
+    private long heartbeatTime = 5000;
+    private boolean isConnect;
+    private OnSocketReadListener onSocketReadListener;
+    private OnConnectStateListener onConnectStateListener;
+
+    private ConnectThread connectThread;
+    private BufferedReader bufferedReader;
+//    private DataInputStream dataInputStream;
+    private OutputStream outputStream;
+    private Socket socket;
+    private ReadThread readThread;
+    private HeartbeatThread heartbeatThread;
+
+    @Override
+    public int start() {
+        startConnectThread();
+        return 0;
+    }
+
+    @Override
+    public int release() {
+
+        stopConnectThread();
+        stopReadThread();
+        stopHeartbeatThread();
+
+        if (bufferedReader != null) {
+            try {
+                bufferedReader.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            bufferedReader = null;
+        }
+        if (outputStream != null) {
+            try {
+                outputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            outputStream = null;
+        }
+
+        if (socket != null) {
+            try {
+                socket.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            socket = null;
+        }
+
+        isConnect = false;
+
+        onConnectState(false);
+        return 0;
+    }
+
+    @Override
+    public int send(byte[] datas) {
+//         String data = HexUtil.formatHexString(datas, false);
+//        Log.d("tcp发送数据", "数据: " + data);
+            int ret = 0;
+            try {
+                ret = sendData(datas);
+                if(ret != 0){
+                    //重新连接
+                    log("发送失败,重新连接");
+                    release();
+                }
+            } catch (Exception e) {
+                //连接失败重新连接
+                release();
+                e.printStackTrace();
+            }
+            return ret;
+
+    }
+
+    /**
+     * 连接线程
+     */
+    class ConnectThread extends Thread {
+        private boolean threadBool = false;
+        private int timeout = 5000;
+        private Socket socketTemp;
+        private boolean isConnected = false; //是否正在连接
+
+        @Override
+        public void run() {
+            super.run();
+            threadBool = true;
+            isConnected = true;
+            while (threadBool) {
+                try {
+                    socketTemp = new Socket();
+                    socketTemp.connect(new InetSocketAddress(ip, port), timeout);
+                    isConnected = false;
+                    socket = socketTemp;
+                    bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+                    outputStream = socket.getOutputStream();
+                    startReadThread();
+                    startHeartbeatThread();
+                    log("连接成功");
+                    isConnect = true;
+                    onConnectState(true);
+                    stopThread();
+                    break;
+                } catch (IOException e) {
+                    //e.printStackTrace();
+                    log("连接失败重新连接:" + ip + ":" + port);
+                }
+
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            log("退出连接线程"+ip+":"+port);
+        }
+
+        public void stopThread() {
+            threadBool = false;
+            Log.e("xxxx","");
+            if (isConnected) {
+                if (socketTemp != null) {
+                    try {
+                        socketTemp.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            interrupt();
+        }
+    }
+
+
+    private void log(String str) {
+        System.out.println(str);
+    }
+
+    private void startConnectThread() {
+        stopConnectThread();
+        connectThread = new ConnectThread();
+        connectThread.start();
+    }
+
+    private void stopConnectThread() {
+        if (connectThread != null) {
+            connectThread.stopThread();
+            connectThread = null;
+        }
+    }
+
+
+
+    void onConnectState(boolean connectState) {
+        if (onConnectStateListener != null) {
+            onConnectStateListener.onConnectState(this, connectState);
+        }
+    }
+
+    private void startHeartbeatThread() {
+        stopHeartbeatThread();
+        heartbeatThread = new HeartbeatThread();
+        heartbeatThread.start();
+    }
+
+    private void stopHeartbeatThread() {
+        if (heartbeatThread != null) {
+            heartbeatThread.stopThread();
+            heartbeatThread = null;
+        }
+    }
+
+    synchronized public int sendData(byte[] bytes){
+        int ret = 0;
+        if(bytes == null || bytes.length == 0) return ret;
+        try {
+            if (outputStream != null) {
+                outputStream.write(bytes);
+                outputStream.flush();
+                ret = 0;
+            }else ret = -1;
+        } catch (IOException e) {
+            e.printStackTrace();
+            ret = -2;
+        }
+        return ret;
+    }
+
+    /**
+     * 心跳线程
+     */
+    class HeartbeatThread extends Thread {
+
+        private boolean threadBool = false;
+
+        @Override
+        public void run() {
+            super.run();
+            threadBool = true;
+            while (threadBool) {
+                Log.e("sss777","心跳线程");
+                int ret = sendData(getHeartbeat());
+                if(ret != 0){
+                    stopThread();
+                    //重新连接
+                    log("发送失败,重新连接");
+                    release();
+                    startConnectThread();
+                    break;
+                }
+                try {
+                    Thread.sleep(heartbeatTime);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+            log("退出心跳线程");
+        }
+
+        public void stopThread() {
+            threadBool = false;
+            interrupt();
+        }
+    }
+
+
+    private void startReadThread() {
+        stopReadThread();
+        readThread = new ReadThread();
+        readThread.start();
+    }
+
+    private void stopReadThread() {
+        if (readThread != null) {
+            readThread.stopThread();
+            readThread = null;
+        }
+    }
+    private boolean threadBool = false;
+    private boolean isReceivingPacket = false;
+    private ByteArrayOutputStream tempBuffer = new ByteArrayOutputStream();
+
+    /**
+     * 读取数据线程
+     */
+    class ReadThread extends Thread {
+
+        private boolean threadBool = false;
+
+        @Override
+        public void run() {
+            super.run();
+//            Log.e("sss","读取数据线程");
+            threadBool = true;
+            while (threadBool) {
+//                Log.e("sss111","读取数据线程");
+                if (bufferedReader != null) {
+                    Log.e("sss222", "读取数据线程");
+                    try {
+                        String str = bufferedReader.readLine();
+                        disposeReadData(str.getBytes());
+                        Log.e("sss333", "读取数据线程");
+                    } catch (Exception e) {
+                        Log.e("sss44444", "读取数据线程");
+                        e.printStackTrace();
+                        stopThread();
+                    }
+                }
+//            log("退出读取数据线程");
+            }
+        }
+
+        public void stopThread() {
+            threadBool = false;
+            interrupt();
+        }
+    }
+
+    private void disposeReadData(byte[] bytes) {
+//        Log.e("sss666","获取的字节");
+        onSocketRead(bytes);
+    }
+    private void onSocketRead(byte[] bytes){
+//        Log.e("sss555","读取数据线程"+onSocketReadListener);
+        if(onSocketReadListener != null){
+            onSocketReadListener.onSocketRead(this, bytes);
+        }
+    }
+
+    @Override
+    public boolean isConnect() {
+        return isConnect;
+    }
+
+    @Override
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    @Override
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    @Override
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+    @Override
+    public void setHeartbeat(byte[] heartbeat) {
+        this.heartbeat = heartbeat;
+    }
+
+    @Override
+    public void setHeartbeatTime(long heartbeatTime) {
+        this.heartbeatTime = heartbeatTime;
+    }
+
+    @Override
+    public long getHeartbeatTime() {
+        return heartbeatTime;
+    }
+
+    @Override
+    public byte[] getHeartbeat() {
+        return heartbeat;
+    }
+
+    @Override
+    public String getIp() {
+        return ip;
+    }
+
+    @Override
+    public int getPort() {
+        return port;
+    }
+
+    @Override
+    public String getTag() {
+        return tag;
+    }
+
+    @Override
+    public void setOnSocketReadListener(OnSocketReadListener onSocketReadListener) {
+        this.onSocketReadListener = onSocketReadListener;
+    }
+
+    @Override
+    public void setOnConnectStateListener(OnConnectStateListener onConnectStateListener) {
+        this.onConnectStateListener = onConnectStateListener;
+    }
+}

+ 194 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/tcp/VisitTcpClient.java

@@ -0,0 +1,194 @@
+package com.wdkl.ncs.android.component.nursehome.visit.tcp;
+
+import android.util.Log;
+
+import com.alibaba.fastjson.JSONObject;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.TcpClientHandler;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpSendModel;
+import com.wdkl.ncs.android.middleware.tcp.util.DateUtil;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.concurrent.TimeUnit;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.CharsetUtil;
+
+//单例
+public class VisitTcpClient {
+   private String TAG = VisitTcpClient.class.getSimpleName();
+
+    private NioEventLoopGroup visitworkGroup;
+    public  Channel channel;
+    private Bootstrap visitbootstrap;
+
+    //数据处理
+    private VisitTcpClientHandler visittcpClientHandler = new VisitTcpClientHandler();
+    //是否运行中
+    public boolean isRunning = false;
+    //重试间隔
+    private Integer retrySeconds = 5;
+    //重试计数
+    private Integer retryTimes = 1;
+    private Integer reconnetTimes = 0;
+    //tcp是否完成初始化
+    private boolean inited = false;
+    private boolean connecting = false;
+
+
+    //单例
+    private static class VisitTcpClientHolder{
+        private static VisitTcpClient instance = new VisitTcpClient();
+    }
+
+    public static VisitTcpClient getInstance(){
+        return VisitTcpClientHolder.instance;
+    }
+
+    //初始化Netty Tcp Client 并连接
+    public void init(String serverIP, Integer serverPort, Integer heartBeatSeconds) {
+        Log.i(TAG, "开始连接" +serverIP+serverPort);
+        final Integer hbSeconds = heartBeatSeconds;
+        visitworkGroup = new NioEventLoopGroup(2);
+        visitbootstrap = new Bootstrap();
+        visitbootstrap.group(visitworkGroup)
+                .channel(NioSocketChannel.class)
+                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15*1000)
+                .option(ChannelOption.SO_KEEPALIVE,true)
+                .handler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(SocketChannel socketChannel) throws Exception {
+                        // 这里将LengthFieldBasedFrameDecoder添加到pipeline的首位,因为其需要对接收到的数据
+                        // 进行长度字段解码,这里也会对数据进行粘包和拆包处理
+                        socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(2048, 0, 2, 0, 2));
+                        // LengthFieldPrepender是一个编码器,主要是在响应字节数据前面添加字节长度字段
+                        socketChannel.pipeline().addLast(new LengthFieldPrepender(2));
+                        //心跳包应当小于服务器间隔
+//                        socketChannel.pipeline().addLast(new IdleStateHandler(0, hbSeconds, 0, TimeUnit.SECONDS));
+                        socketChannel.pipeline().addLast(new IdleStateHandler(hbSeconds*2, hbSeconds, 0, TimeUnit.SECONDS));
+                        socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
+                        socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
+                        socketChannel.pipeline().addLast(visittcpClientHandler);
+                    }
+                }).remoteAddress(serverIP,serverPort);
+        inited = true;
+        doConnect();
+        System.out.println("connect server host: " + serverIP + ", port: " + serverPort);
+    }
+
+    //独立连接方法,用于重新连接
+    public synchronized void doConnect(){
+        if (!inited) {
+            System.out.println("tcp is not initialized");
+            return;
+        }
+
+        if (channel != null && (channel.isActive() || channel.isOpen())) {
+            System.out.println("TcpClient connecting");
+            return;
+        }
+
+        //正在连接
+        if (connecting || Constant.Visit_TCP_CONNECTED) {
+            System.out.println("tcp is connecting or connected");
+            return;
+        }
+        connecting = true;
+
+        System.out.println("tcp connect start");
+        ChannelFuture visitfuture = visitbootstrap.connect().addListener(new ChannelFutureListener() {
+            @Override
+            public void operationComplete(ChannelFuture channelFuture) throws Exception {
+                connecting = false;
+                if (channelFuture.isSuccess()){
+                    isRunning = true;
+                    channel = channelFuture.channel();
+                    retryTimes = 1;
+                    System.out.println("visttcp connect success");
+                } else {
+                    //连接失败时的处理
+                    isRunning = false;
+                    System.out.println("visttcp connect retry : " + retryTimes);
+                    channelFuture.channel().eventLoop().schedule(new Runnable() {
+                        @Override
+                        public void run() {
+                            retryTimes++;
+                            if (retryTimes > 30) { //重试30次还没连成功,等10分钟后再试
+                                retryTimes=1;
+                                reconnetTimes++;
+                                channelFuture.channel().eventLoop().schedule(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        System.out.println("vistTcpClientHandler 重新连接,第" + retryTimes + "次" + ", 重试" + reconnetTimes + "次");
+                                        if (reconnetTimes > 2) {
+                                            EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_RESTART_APP));
+                                        } else {
+                                            doConnect();
+                                        }
+                                    }
+                                }, 60*2, TimeUnit.SECONDS);
+
+                            }else{
+                                doConnect();
+                            }
+
+                        }
+                    }, 5, TimeUnit.SECONDS);
+                }
+            }
+        });
+    }
+
+    //发送消息,线程安全
+    public void sendTcp(TcpModel tcpModel, Boolean reSend, TcpCallback transaction) {
+        String tcpString = JSONObject.toJSONString(tcpModel);
+
+        if (reSend){
+            TcpSendModel model = new TcpSendModel();
+            model.setMsg(tcpString);
+            model.setTid(tcpModel.getTid());
+            model.setTs(DateUtil.getDateline());
+            //启动定时任务
+            visittcpClientHandler.handleReSend(model);
+        }
+        sendMsg(tcpString, transaction);
+    }
+
+    //发送消息,线程安全
+    public synchronized void sendMsg(String content, TcpCallback transaction) {
+        //System.out.println("send tcp msg = [" + content + "]");
+        if (visittcpClientHandler != null) {
+            visittcpClientHandler.sendMsg(content, transaction);
+        }
+    }
+
+    //发送消息,线程安全
+    public synchronized void sendMsg(String content){
+        if (visittcpClientHandler != null) {
+            visittcpClientHandler.sendMsg(content);
+        }
+    }
+
+
+    public Channel getChannel() {
+        return channel;
+    }
+}

+ 249 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/visit/tcp/VisitTcpClientHandler.java

@@ -0,0 +1,249 @@
+package com.wdkl.ncs.android.component.nursehome.visit.tcp;
+
+import android.util.Log;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.base.Strings;
+import com.wdkl.ncs.android.middleware.BuildConfig;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+import com.wdkl.ncs.android.middleware.tcp.TaskSchedule;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpSendModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.tcp.util.DateUtil;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.CharsetUtil;
+import io.netty.util.ReferenceCountUtil;
+
+@ChannelHandler.Sharable
+public class VisitTcpClientHandler extends SimpleChannelInboundHandler<String> {
+    private static final String TAG = VisitTcpClientHandler.class.getSimpleName();
+    ChannelHandlerContext ctx;
+    //重连间隔
+    private static Integer retrySeconds = 5;
+    //重连计数
+    private static Integer retryTimes = 0;
+    //总共总连接次数,总连接次数过多可能是网络不稳定
+    private static Integer totalRetryTimes = 0;
+    //是否连接成功
+    private static Boolean connected = false;
+    public static Boolean getConnected(){
+        return connected;
+    }
+
+    private Integer stopSendSeconds = 20;
+
+    //存储回调 callback, key: tid
+    private static ConcurrentHashMap<String, TcpCallback> trantCache = new ConcurrentHashMap<>();
+    //key: tid。用于重发
+    static ConcurrentHashMap<String, TcpSendModel> tcpSendCache = new ConcurrentHashMap<>();
+
+    //连接成功执行的方法
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        super.channelActive(ctx);
+        Log.i(TAG, "探视tcp连接成功");
+        this.ctx = ctx;
+        connected = true;
+        retryTimes = 0;
+        Constant.Visit_TCP_CONNECTED = true;
+//        EventBus.getDefault().post(new MessageEvent(1, Constant.EVENT_TCP_STATE));
+        //寻找
+//        TcpModel tcpModel = DeviceUtil.deviceConnect4(Constant.union_id,Constant.DEVICE_REGISTER_ID);
+        TcpModel tcpModel = DeviceUtil.deviceConnect4("610a4ebae21b840007887b95",Constant.DEVICE_REGISTER_ID);
+        VisitTcpClient.getInstance().sendMsg(tcpModel.toJson());
+    }
+
+    //断开连接
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        super.channelInactive(ctx);
+        this.ctx = null;
+        connected = false;
+        Constant.Visit_TCP_CONNECTED = false;
+        EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_TCP_STATE));
+        Log.i(TAG, "失去连接");
+        VisitTcpClient.getInstance().doConnect();
+    }
+
+    //读取String消息
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, String source) throws Exception {
+
+        try {
+            //System.out.println("tcp msg = [" + source + "]");
+            if (source.equals("1")) {
+                connected = true;
+                Constant.Visit_TCP_CONNECTED = true;
+                Log.e(TAG, "收到服务器返回的心跳" + "source " + source);
+            } else {
+                Log.i(TAG, "received msg: " + source);
+                TcpModel tcpModel = TcpModel.getModelByJson(source);
+                if (tcpModel != null) {
+                    //Log.i(TAG, "received type==" + tcpModel.getType() + ", action==" + tcpModel.getAction() + ", tid===" + tcpModel.getTid());
+                    switch (tcpModel.getType()) {
+                        case CALLBACK:
+                            //收到ack说明消息已到达服务器,可以取消重发,收到success说明消息正常处理
+                            if (tcpModel.getAction() == TcpAction.CallbackAction.ACK) {
+                                //服务器已收到tcp消息 收到ack不能删除缓存
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    // 取消定时任务
+                                    tcpSendCache.remove(tcpModel.getTid());
+                                    TcpCallback transaction = trantCache.get(tcpModel.getTid());
+                                    if (transaction != null) {
+                                        transaction.onAck();
+                                    }
+                                }
+                            } else if (tcpModel.getAction() == TcpAction.CallbackAction.SUCCESS) {
+                                //回调成功
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    TcpCallback transaction = trantCache.get(tcpModel.getTid());
+                                    if (transaction != null) {
+                                        JSONObject jsonObject = new JSONObject();
+                                        jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_SUCCESS);
+                                        if (tcpModel.getData() != null) {
+                                            jsonObject.put(TcpCallback.CALLBACK_DATA, tcpModel.getData());
+                                        }
+                                        transaction.onSuccess(jsonObject);
+                                        trantCache.remove(tcpModel.getTid());
+                                    }
+                                }
+                            } else if (tcpModel.getAction() == TcpAction.CallbackAction.FAILED) {
+
+                                //回调失败
+                                if (!Strings.isNullOrEmpty(tcpModel.getTid())) {
+                                    TcpCallback transaction = trantCache.get(tcpModel.getTid());
+                                    if (transaction != null) {
+                                        JSONObject jsonObject = new JSONObject();
+                                        if (tcpModel.getData() != null) {
+                                            jsonObject.put(TcpCallback.CALLBACK, tcpModel.getData().toString());
+                                        } else {
+                                            jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_FAIL);
+                                        }
+                                        transaction.onFailed(jsonObject);
+                                        trantCache.remove(tcpModel.getTid());
+                                    }
+                                }
+                            }
+                            break;
+
+                        default:
+                            //原来的方式处理
+                            TcpModel responseTcpModel = DeviceChannel.handleTcpReceived(tcpModel);
+                            if (responseTcpModel != null) {
+                                ctx.writeAndFlush(responseTcpModel.toJson());
+                            } else {
+                                ReferenceCountUtil.release(source);
+                            }
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    //写心跳包。没有消息发送时,每间隔一定时间会由此方法向服务端发送心跳
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            if (event.state() == IdleState.WRITER_IDLE) { //超时未执行写操作,在指定的超时时间内未有写操作,要发送心跳包,告诉服务器连接还存活。服务器收到心跳立马回应,正常客户端收到后执行读操作,
+                ctx.writeAndFlush("0");                //这种情况下不会引发READER_IDLE事件。如果服务器因为网络或其他原因导致回应的心跳,客户端没有收到,在超过写超时时间2个周期后依然没有收到,
+            } else if (event.state() == IdleState.READER_IDLE) { //认为服务不可用,主动断开连接
+                Log.i(TAG, "TcpClientHandler ===> READER_IDLE");
+                ctx.close();
+            } else if (event.state() == IdleState.ALL_IDLE) {
+                Log.i(TAG, "TcpClientHandler ===> ALL_IDLE");
+            }
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        cause.printStackTrace();
+        ctx.close();
+        Constant.TCP_CONNECTED = false;
+        EventBus.getDefault().post(new MessageEvent(0, Constant.EVENT_TCP_STATE));
+        connected = false;
+        System.out.println("失去连接,错误引起");
+    }
+
+    //发送消息,不直接调用些方法,调用TcpClient中的发送消息
+    public void sendMsg(String msg){
+        if (ctx == null) {
+            System.out.println("ctx is null");
+            return;
+        }
+
+        Log.i(TAG, "send tcp msg ==> " + msg);
+        ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
+    }
+
+    //使用带callback和tid方式发送消息
+    public synchronized boolean sendMsg(String msg, TcpCallback transaction){
+        if (ctx == null){
+            return false;
+        }
+
+        Log.i(TAG, "send tcp msg ==> " + msg + ", transaction ==> " + transaction);
+        //ctx.writeAndFlush(msg);
+        ctx.writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
+        if (transaction!=null) {
+            trantCache.put(transaction.getTid(), transaction);
+        }
+        return true;
+    }
+
+    //发送消息时缓存定时任务
+    public void handleReSend(TcpSendModel model){
+        tcpSendCache.put(model.getTid(), model);
+        TaskSchedule.getInstance().scheduleTask("TCP_MESSAGE_REPEAT" + model.getTid(), new Runnable() {
+            @Override
+            public void run() {
+                TcpSendModel m = tcpSendCache.get(model.getTid());
+                if (m != null){
+                    System.out.println("定时任务:" + m.getTid() + "...........");
+                    long time = DateUtil.getDateline() - m.getTs();
+                    //比对时间
+                    if (time < stopSendSeconds){
+                        //因为是重发,所以不需要再缓存 callback
+                        System.out.println("重发消息");
+                        sendMsg(model.getMsg(), null);
+                    } else { //超时处理
+                        //关闭定时任务,提示掉线?
+                        System.out.println("取消定时任务,提示掉线");
+                        TaskSchedule.getInstance().cancelTask("TCP_MESSAGE_REPEAT" + model.getTid());
+                        //这里可以执行失败的方法
+                        TcpCallback transaction = trantCache.get(model.getTid());
+                        if (transaction!=null){
+                            JSONObject jsonObject = new JSONObject();
+                            jsonObject.put(TcpCallback.CALLBACK, TcpCallback.CALLBACK_FAIL);
+                            transaction.onFailed(jsonObject);
+                        }
+                        trantCache.remove(model.getTid());
+                        tcpSendCache.remove(model.getTid());
+                    }
+                } else {    //缓存不存在,取消定时
+                    System.out.println("缓存不存在,取消定时");
+                    TaskSchedule.getInstance().cancelTask("TCP_MESSAGE_REPEAT" + model.getTid());
+                }
+            }
+        },5000L, 5000L);
+    }
+}

+ 44 - 20
android_host/src/main/res/layout/activity_new_nurse_home.xml

@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    >
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -174,25 +176,47 @@
                         android:textSize="20sp"
                         android:textStyle="bold" />
 
-                    <RadioButton
-                        android:id="@+id/menu_led"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginLeft="@dimen/d15"
-                        android:layout_marginTop="10dp"
-                        android:layout_marginRight="@dimen/d15"
-                        android:background="@drawable/selector_menu_bg_color"
-                        android:button="@null"
-                        android:ellipsize="end"
-                        android:gravity="center"
-                        android:padding="10dp"
-                        android:paddingTop="@dimen/d10"
-                        android:paddingBottom="@dimen/d10"
-                        android:singleLine="true"
-                        android:text="@string/str_led"
-                        android:textColor="@drawable/selector_menu_text_color"
-                        android:textSize="20sp"
-                        android:textStyle="bold" />
+                    <androidx.constraintlayout.widget.ConstraintLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content">
+
+                        <RadioButton
+                            android:id="@+id/menu_led"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginLeft="@dimen/d15"
+                            android:layout_marginTop="10dp"
+                            android:layout_marginRight="@dimen/d15"
+                            android:background="@drawable/selector_menu_bg_color"
+                            android:button="@null"
+                            android:ellipsize="end"
+                            android:gravity="center"
+                            android:padding="10dp"
+                            android:paddingTop="@dimen/d10"
+                            android:paddingBottom="@dimen/d10"
+                            android:singleLine="true"
+                            android:text="@string/str_visit"
+                            android:textColor="@drawable/selector_menu_text_color"
+                            android:textSize="20sp"
+                            android:textStyle="bold"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toTopOf="parent" />
+
+
+
+                        <ImageView
+                            android:id="@+id/redDot"
+                            android:layout_width="10dp"
+                            android:layout_height="10dp"
+                            android:layout_marginStart="8dp"
+                            android:layout_marginTop="8dp"
+                            android:visibility="gone"
+                            android:background="@drawable/shape_untreated_quantity"
+                            app:layout_constraintEnd_toEndOf="@id/menu_led"
+                            app:layout_constraintTop_toTopOf="@id/menu_led" />
+
+                    </androidx.constraintlayout.widget.ConstraintLayout>
+
 
                     <RadioButton
                         android:id="@+id/menu_more"

+ 246 - 0
android_host/src/main/res/layout/adapter_visit_frame_part.xml

@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/rl_bed_info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/shape_bed_bg"
+        android:layout_marginBottom="@dimen/d15"
+        android:paddingBottom="@dimen/d20">
+
+        <TextView
+            android:id="@+id/visit_region"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d20"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="探视床位:"
+            android:textColor="@color/black"
+            android:textSize="@dimen/font_size_18" />
+        <TextView
+            android:id="@+id/visit_region_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d20"
+            android:layout_toRightOf="@+id/visit_region"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:text="张芳鹏 32床"
+            android:textStyle="bold"
+            android:textColor="@color/color_red"
+            android:textSize="@dimen/font_size_18" />
+
+
+        <TextView
+            android:id="@+id/visit_name_tit"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d8"
+            android:text="探视人:"
+            android:textSize="@dimen/font_size_18"
+            android:layout_below="@+id/visit_region"
+            android:textColor="@color/black" />
+        <TextView
+            android:id="@+id/visit_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_toRightOf="@+id/visit_name_tit"
+            android:layout_marginTop="@dimen/d8"
+            android:text="张宏伟"
+            android:textStyle="bold"
+            android:textSize="@dimen/font_size_18"
+            android:layout_below="@+id/visit_region"
+            android:textColor="@color/orange" />
+        <TextView
+            android:id="@+id/visit_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d8"
+            android:layout_below="@+id/visit_region"
+            android:layout_toRightOf="@+id/visit_name"
+            android:text="关系 :父亲"
+            android:textSize="@dimen/font_size_18"
+            android:textStyle="bold"
+            android:textColor="@color/color_main" />
+        <TextView
+            android:id="@+id/visit_sk_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="@dimen/d40"
+            android:text="已完成"
+            android:textSize="@dimen/font_size_40"
+            android:textStyle="bold"
+            android:visibility="gone"
+            android:textColor="@color/color_main" />
+
+
+        <TextView
+            android:id="@+id/visit_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/d18"
+            android:layout_marginTop="@dimen/d5"
+            android:layout_below="@+id/visit_name"
+            android:text="探视时间:每天10:00~12:00"
+            android:textSize="@dimen/font_size_18"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="@color/black" />
+
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:layout_centerVertical="true"
+            android:layout_alignParentRight="true"
+
+            >
+
+            <LinearLayout
+                android:id="@+id/visit_delete"
+                android:layout_width="@dimen/d100"
+                android:layout_height="@dimen/d45"
+                android:gravity="center_vertical"
+                android:clickable="true"
+                android:background="@drawable/shape_other_item_bg"
+                android:orientation="horizontal"
+                android:visibility="gone">
+
+                <ImageView
+                    android:layout_width="@dimen/d32"
+                    android:layout_height="@dimen/d32"
+                    android:background="@mipmap/shan"
+                    android:layout_marginLeft="@dimen/d10" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="@dimen/d5"
+                    android:text="删除"
+                    android:textColor="@color/black"
+                    android:textSize="@dimen/font_size_18"
+                    android:textStyle="bold" />
+            </LinearLayout>
+            <LinearLayout
+                android:id="@+id/visit_ting"
+                android:layout_width="@dimen/d100"
+                android:layout_height="@dimen/d45"
+                android:gravity="center_vertical"
+                android:layout_centerVertical="true"
+                android:layout_marginRight="@dimen/d18"
+                android:background="@drawable/shape_other_item_bg"
+                android:orientation="horizontal"
+                >
+
+                <ImageView
+                    android:layout_width="@dimen/d32"
+                    android:layout_height="@dimen/d32"
+                    android:background="@mipmap/visit_call_yes"
+                    android:layout_marginLeft="@dimen/d10" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="查看"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:textSize="@dimen/font_size_18"
+                    android:layout_marginLeft="@dimen/d5" />
+            </LinearLayout>
+            <LinearLayout
+                android:id="@+id/visit_play"
+                android:layout_width="@dimen/d100"
+                android:layout_height="@dimen/d45"
+                android:gravity="center_vertical"
+                android:layout_centerVertical="true"
+                android:layout_marginRight="@dimen/d18"
+                android:background="@drawable/shape_other_item_bg"
+                android:orientation="horizontal"
+                >
+
+                <ImageView
+                    android:layout_width="@dimen/d32"
+                    android:layout_height="@dimen/d32"
+                    android:background="@mipmap/visit_call_no"
+                    android:layout_marginLeft="@dimen/d10" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="挂断"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:textSize="@dimen/font_size_18"
+                    android:layout_marginLeft="@dimen/d5" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/visit_stop"
+                android:layout_width="@dimen/d100"
+                android:layout_height="@dimen/d45"
+                android:gravity="center_vertical"
+                android:layout_centerVertical="true"
+                android:layout_marginRight="@dimen/d18"
+                android:background="@drawable/shape_other_item_bg"
+                android:orientation="horizontal"
+                android:visibility="gone"
+                >
+
+                <ImageView
+                    android:layout_width="@dimen/d32"
+                    android:layout_height="@dimen/d32"
+                    android:background="@mipmap/visit_yes"
+                    android:layout_marginLeft="@dimen/d10" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="@dimen/d5"
+                    android:text="通过"
+                    android:textColor="@color/black"
+                    android:textSize="@dimen/font_size_18"
+                    android:textStyle="bold" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/visit_settings"
+                android:layout_width="@dimen/d100"
+                android:layout_height="@dimen/d45"
+                android:gravity="center_vertical"
+                android:layout_centerVertical="true"
+                android:layout_marginRight="@dimen/d18"
+                android:background="@drawable/shape_other_item_bg"
+                android:orientation="horizontal"
+                android:visibility="gone"
+                >
+
+                <ImageView
+                    android:layout_width="@dimen/d32"
+                    android:layout_height="@dimen/d32"
+                    android:background="@mipmap/visit_reject"
+                    android:layout_marginLeft="@dimen/d10" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="拒绝"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:textSize="@dimen/font_size_18"
+                    android:layout_marginLeft="@dimen/d5" />
+            </LinearLayout>
+        </LinearLayout>
+
+
+
+    </RelativeLayout>
+</layout>

+ 106 - 0
android_host/src/main/res/layout/fragment_visit_agore_call.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@mipmap/call_bg">
+
+
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            >
+
+            <FrameLayout
+                android:id="@+id/local_video_view_user"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+
+
+            <FrameLayout
+                android:id="@+id/remote_video_view_container"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                tools:ignore="MissingConstraints" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/visit_call_bt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="@dimen/d50"
+            android:layout_centerHorizontal="true"
+            >
+            <Chronometer
+                android:id="@+id/visit_voice_call_timer"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:text="00:00"
+                android:textColor="@color/white"
+                android:textSize="24sp" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="32dp">
+
+                <ImageView
+                    android:id="@+id/visit_voice_call_exit"
+                    android:layout_width="84dp"
+                    android:layout_height="84dp"
+                    android:layout_marginRight="40dp"
+                    android:clickable="true"
+                    android:src="@mipmap/visit_exit"
+                    />
+
+
+                <ImageView
+                    android:id="@+id/visit_voice_call_hangup"
+                    android:layout_width="80dp"
+                    android:layout_height="80dp"
+                    android:layout_marginLeft="@dimen/d20"
+                    android:clickable="true"
+                    android:visibility="gone"
+                    android:src="@drawable/selector_call_hangup" />
+
+                <ImageView
+                    android:id="@+id/visit_call_speaker"
+                    android:layout_width="84dp"
+                    android:layout_height="84dp"
+                    android:layout_marginStart="40dp"
+                    android:clickable="true"
+                    android:visibility="gone"
+                    android:src="@drawable/av_speaker_selector"
+                    />
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/visit_call_txt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="双方暂未开启通话"
+            android:textStyle="bold"
+            android:textSize="@dimen/font_size_60"
+            android:textColor="@color/white"
+            android:gravity="center"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            />
+
+
+
+
+    </RelativeLayout>
+</layout>

+ 153 - 0
android_host/src/main/res/layout/fragment_visit_agore_main.xml

@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="8dp"
+        android:orientation="vertical"
+        android:background="#EAF2F9">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/d20">
+
+            <LinearLayout
+                android:id="@+id/visit_main_list_bt"
+                android:layout_width="0dp"
+                android:layout_height="90dp"
+                android:layout_weight="1"
+                android:clickable="true"
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                android:background="@drawable/shape_bed_bg"
+                android:layout_marginRight="@dimen/d15">
+
+                <ImageView
+                    android:id="@+id/visit_main_list_img"
+                    android:layout_width="@dimen/d60"
+                    android:layout_height="@dimen/d60"
+                    android:layout_marginLeft="@dimen/d45"
+                    android:background="@mipmap/visit_list_img"/>
+
+                <TextView
+                    android:id="@+id/visit_main_list_bt_tx"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:layout_marginLeft="@dimen/d16"
+                    android:text="探视中"
+                    android:textSize="18sp" />
+
+                <ImageView
+                    android:id="@+id/visit_main_list_bt_img"
+                    android:layout_width="@dimen/d18"
+                    android:layout_height="@dimen/d18"
+                    android:layout_marginLeft="@dimen/d20"
+                    android:background="@mipmap/jr" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/visit_main_sk_bt"
+                android:layout_width="0dp"
+                android:layout_height="90dp"
+                android:layout_weight="1"
+                android:clickable="true"
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                android:background="@drawable/shape_bed_bg"
+                android:layout_below="@+id/tv_nursing_code">
+
+                <ImageView
+                    android:id="@+id/visit_main_sk_img"
+                    android:layout_width="@dimen/d60"
+                    android:layout_height="@dimen/d60"
+                    android:layout_marginLeft="@dimen/d44"
+                    android:background="@mipmap/visit_sk_img"/>
+
+                <TextView
+                    android:id="@+id/visit_main_sk_bt_tx"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:layout_marginLeft="@dimen/d16"
+                    android:text="探视审核"
+                    android:textSize="18sp" />
+
+                <ImageView
+                    android:id="@+id/visit_main_sk_bt_img"
+                    android:layout_width="@dimen/d18"
+                    android:layout_height="@dimen/d18"
+                    android:layout_marginLeft="@dimen/d20"
+                    android:background="@mipmap/jr" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/visit_main_all_bt"
+                android:layout_width="0dp"
+                android:layout_height="90dp"
+                android:layout_weight="1"
+                android:clickable="true"
+                android:layout_marginLeft="@dimen/d15"
+                android:orientation="horizontal"
+                android:gravity="center_vertical"
+                android:background="@drawable/shape_bed_bg"
+                >
+
+                <ImageView
+                    android:id="@+id/visit_main_all_img"
+                    android:layout_width="@dimen/d60"
+                    android:layout_height="@dimen/d60"
+                    android:layout_marginLeft="@dimen/d44"
+                    android:background="@mipmap/visit_all_img"/>
+
+                <TextView
+                    android:id="@+id/visit_main_all_bt_tx"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:layout_marginLeft="@dimen/d16"
+                    android:text="全部探视"
+                    android:textSize="18sp" />
+
+                <ImageView
+
+                    android:id="@+id/visit_main_all_bt_img"
+                    android:layout_width="@dimen/d18"
+                    android:layout_height="@dimen/d18"
+                    android:layout_marginLeft="@dimen/d20"
+                    android:background="@mipmap/jr" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/visit_main_emptyImageView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:src="@mipmap/no_information"
+            android:layout_gravity="center"
+            android:visibility="gone"/>
+
+        <com.scwang.smartrefresh.layout.SmartRefreshLayout
+            android:id="@+id/visit_main_refresh"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            bind:srlEnableLoadMore="true"
+            bind:srlEnableRefresh="true">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/visit_main_br_listView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scrollbars="vertical"
+            android:fadeScrollbars="true"
+            android:scrollbarStyle="insideOverlay"
+            android:layout_marginTop="@dimen/d20"
+            />
+
+        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
+    </LinearLayout>
+</layout>

+ 281 - 0
android_host/src/main/res/layout/fragment_visit_through.xml

@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#EAF2F9"
+        android:orientation="vertical"
+        android:gravity="center_horizontal"
+        >
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@drawable/shape_bed_bg"
+            android:layout_marginTop="@dimen/d20"
+            android:layout_marginLeft="@dimen/d15"
+            android:layout_marginRight="@dimen/d24"
+            android:layout_marginBottom="@dimen/d20"
+            >
+
+            <RelativeLayout
+                android:id="@+id/visit_through_ll_1"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                >
+
+
+                <TextView
+                    android:id="@+id/visit_through_tx_1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="*"
+                    android:textSize="@dimen/font_size_18"
+                    android:textColor="@color/txt_number"
+                    android:textStyle="bold"
+                    android:layout_marginTop="@dimen/d24"
+                    android:layout_marginLeft="@dimen/d28"
+                    />
+
+                <TextView
+                    android:id="@+id/visit_through_tx_2"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_toRightOf="@+id/visit_through_tx_1"
+                    android:text="请设置拒绝理由"
+                    android:textSize="@dimen/font_size_18"
+                    android:textColor="@color/black"
+                    android:textStyle="bold"
+                    android:layout_marginTop="@dimen/d24"
+
+                    />
+
+                <EditText
+                    android:id="@+id/visit_through_ed_name"
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/d50"
+                    android:hint="请输入"
+                    android:paddingLeft="@dimen/d26"
+                    android:textSize="@dimen/font_size_18"
+                    android:gravity="center_vertical"
+                    android:layout_below="@+id/visit_through_tx_2"
+                    android:textStyle="bold"
+                    android:background="@drawable/shape_n_login_ed_bg2"
+                    android:layout_marginTop="@dimen/d16"
+                    android:layout_marginLeft="@dimen/d28"
+                    android:layout_marginRight="@dimen/d48"
+                    />
+
+                <RelativeLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_below="@+id/visit_through_ed_name"
+                    android:orientation="horizontal"
+                    android:gravity="center_vertical"
+                    android:layout_marginTop="@dimen/d30"
+                    >
+
+                    <TextView
+                        android:id="@+id/visit_through_tx_3"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                        android:gravity="center"
+                        android:text="病人需要休息"
+                        android:textSize="@dimen/font_size_14"
+                        android:textColor="@color/text_name_color"
+                        android:layout_marginLeft="@dimen/d48"
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+                    <TextView
+                        android:id="@+id/visit_through_tx_4"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                       android:layout_toRightOf="@+id/visit_through_tx_3"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:gravity="center"
+                        android:text="防止感染传播"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+                    <TextView
+                        android:id="@+id/visit_through_tx_5"
+                        android:layout_width="@dimen/d160"
+                        android:layout_height="@dimen/d60"
+                        android:layout_toRightOf="@+id/visit_through_tx_4"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:gravity="center"
+                        android:text="病人特殊治疗中"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+                    <TextView
+                        android:id="@+id/visit_through_tx_6"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:layout_marginTop="@dimen/d40"
+                        android:layout_below="@+id/visit_through_tx_3"
+                        android:gravity="center"
+                        android:text="病人情绪不稳定"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+                    <TextView
+                        android:id="@+id/visit_through_tx_7"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:layout_marginTop="@dimen/d40"
+                        android:layout_below="@+id/visit_through_tx_3"
+                        android:layout_toRightOf="@+id/visit_through_tx_6"
+                        android:gravity="center"
+                        android:text="遵守医院规定"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+                    <TextView
+                        android:id="@+id/visit_through_tx_8"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:layout_marginTop="@dimen/d40"
+                        android:layout_toRightOf="@+id/visit_through_tx_7"
+                        android:layout_below="@+id/visit_through_tx_3"
+                        android:gravity="center"
+                        android:text="保护病人隐私"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+
+                    <TextView
+                        android:id="@+id/visit_through_tx_9"
+                        android:layout_width="@dimen/d180"
+                        android:layout_height="@dimen/d60"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:layout_marginTop="@dimen/d40"
+                        android:layout_below="@+id/visit_through_tx_6"
+                        android:gravity="center"
+                        android:text="急情况处理"
+                        android:textSize="@dimen/font_size_16"
+                        android:textColor="@color/text_name_color"
+                        android:background="@drawable/shape_time_f_bg"
+                        />
+
+                </RelativeLayout>
+            </RelativeLayout>
+
+            <LinearLayout
+                android:id="@+id/visit_through_ll_2"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@drawable/shape_bed_bg"
+                android:layout_marginTop="@dimen/d16"
+                android:layout_marginRight="@dimen/d24"
+                android:layout_marginLeft="@dimen/d15"
+                android:layout_marginBottom="@dimen/d20"
+                android:layout_above="@+id/visit_through_ll_3"
+                android:orientation="vertical"
+                android:visibility="gone"
+                >
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/d10"
+                    >
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="*"
+                        android:textSize="@dimen/font_size_18"
+                        android:textColor="@color/txt_number"
+                        android:textStyle="bold"
+                        android:layout_marginLeft="@dimen/d28"
+                        />
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_toRightOf="@+id/set_br_tx_3"
+                        android:text="请选择探视床位(单选)"
+                        android:textSize="@dimen/font_size_18"
+                        android:textColor="@color/black"
+                        android:textStyle="bold"
+                        />
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="@dimen/d10"
+                    >
+                    <androidx.recyclerview.widget.RecyclerView
+                        android:id="@+id/visit_through_info"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:layout_marginLeft="28dp"
+                        android:layout_marginTop="@dimen/d5"
+                        android:overScrollMode="never"
+                        android:paddingRight="@dimen/d10" />
+
+                </LinearLayout>
+
+
+
+
+            </LinearLayout>
+
+
+            <LinearLayout
+                android:id="@+id/visit_through_ll_3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_centerHorizontal="true"
+                android:layout_marginBottom="@dimen/d40"
+                >
+
+                <TextView
+                    android:id="@+id/visit_through_retun_bt"
+                    android:layout_width="@dimen/d350"
+                    android:layout_height="@dimen/d60"
+                    android:layout_marginRight="@dimen/d22"
+                    android:layout_gravity="center"
+                    android:background="@drawable/shape_password_bt_bg"
+                    android:gravity="center"
+                    android:text="返回界面"
+                    android:textColor="@color/white"
+                    android:textSize="@dimen/font_size_20" />
+
+                <TextView
+                    android:id="@+id/visit_through_save_bt"
+                    android:layout_width="@dimen/d350"
+                    android:layout_height="@dimen/d60"
+                    android:layout_gravity="center"
+                    android:background="@drawable/shape_main_hos_txt_bg"
+                    android:gravity="center"
+                    android:text="确认审核"
+                    android:textColor="@color/white"
+                    android:textSize="@dimen/font_size_20" />
+
+            </LinearLayout>
+
+
+
+
+
+
+        </RelativeLayout>
+
+
+
+
+    </LinearLayout>
+</layout>

+ 1 - 0
android_host/src/main/res/layout/sky_voice_call_layout.xml

@@ -124,6 +124,7 @@
                     android:text="00:00"
                     android:textColor="@color/white"
                     android:textSize="24sp" />
+
                 <LinearLayout
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"

+ 63 - 0
android_host/src/main/res/layout/visit_set_item_lay.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center_vertical"
+    android:layout_margin="@dimen/d25"
+    >
+
+  <LinearLayout
+      android:id="@+id/visit_main_item_bed_ll"
+    android:layout_width="@dimen/d120"
+    android:layout_height="@dimen/d80"
+    android:orientation="vertical"
+    android:background="@drawable/shape_time_f_bg"
+    >
+
+    <RelativeLayout
+        android:id="@+id/home_f_bed_item_linlayout"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/d30"
+        android:background="@drawable/sp_home_bed_item_t_bg"
+        >
+        <TextView
+            android:id="@+id/visit_main_item_bed_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_centerHorizontal="true"
+            android:layout_marginLeft="@dimen/d10"
+            android:text="0156房"
+            android:textColor="@color/white"
+            android:textStyle="bold"
+            android:textSize="@dimen/font_size_16" />
+
+    </RelativeLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        >
+
+        <TextView
+            android:id="@+id/visit_main_item_name"
+            android:layout_width="@dimen/d80"
+            android:layout_height="@dimen/d40"
+            android:text="王医生"
+            android:textSize="@dimen/font_size_24"
+            android:textStyle="bold"
+            android:gravity="center"
+            android:layout_gravity="center"
+            android:textColor="@color/black"/>
+    </LinearLayout>
+
+
+</LinearLayout>
+
+
+</LinearLayout>
+    
+</layout>

二进制
android_host/src/main/res/mipmap-xhdpi/visit_all_img.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_all_img_w.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_call_no.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_call_yes.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_exit.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_list_img.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_list_img_w.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_reject.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_sk_img.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_sk_img_w.png


二进制
android_host/src/main/res/mipmap-xhdpi/visit_yes.png


+ 1 - 0
android_host/src/main/res/values-es/strings.xml

@@ -108,6 +108,7 @@
     <string name="str_manage_host">Administrar host</string>
     <string name="str_trusteeship">Custodia</string>
     <string name="str_led">LED</string>
+    <string name="str_visit">Visita con cita</string>
     <string name="str_settings">Configuración</string>
     <string name="str_delete">Eliminar</string>
     <string name="str_save">Guardar</string>

+ 1 - 0
android_host/src/main/res/values-ru/strings.xml

@@ -109,6 +109,7 @@
     <string name="str_manage_host">Пульт медсестры</string>
     <string name="str_trusteeship">Доверительное управление</string>
     <string name="str_led">светодиод</string>
+    <string name="str_visit">Назначенный визит</string>
     <string name="str_settings">Настройки</string>
     <string name="str_delete">Удалить</string>
     <string name="str_save">Сохранить</string>

+ 1 - 0
android_host/src/main/res/values-zh/strings.xml

@@ -110,6 +110,7 @@
     <string name="str_manage_host">总控主机</string>
     <string name="str_trusteeship">托管</string>
     <string name="str_led">点阵屏</string>
+    <string name="str_visit">预约探视</string>
     <string name="str_settings">设置</string>
     <string name="str_delete">删除</string>
     <string name="str_save">保存</string>

+ 1 - 0
android_host/src/main/res/values/strings.xml

@@ -111,6 +111,7 @@
     <string name="str_manage_host">Manager Host</string>
     <string name="str_trusteeship">Trusteeship</string>
     <string name="str_led">LED</string>
+    <string name="str_visit">visit</string>
     <string name="str_settings">Settings</string>
     <string name="str_delete">Delete</string>
     <string name="str_save">Save</string>

+ 2 - 7
android_mobile/build.gradle

@@ -35,14 +35,9 @@ android {
         buildConfigField "String", "BUILD_TIME", getDate()
         buildConfigField 'String', 'VERSION_NAME', "\"${project.rootProject.ext.app_version}\""
         buildConfigField 'String', 'VERSION_CODE', "\"${project.rootProject.ext.app_version_code}\""
-        buildConfigField 'String', 'iscallingdoor', "\"${project.rootProject.ext.callingdoor}\""
-        buildConfigField 'String', 'isandroid_bed', "\"${project.rootProject.ext.android_bed}\""
-        buildConfigField 'String', 'isandroid_host', "\"${project.rootProject.ext.android_host}\""
-        buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
-        buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
-        buildConfigField 'String', 'open_sleep', "\"${project.rootProject.ext.open_sleep}\""
-        buildConfigField 'String', 'open_433', "\"${project.rootProject.ext.open_433}\""
+        buildConfigField 'String', 'device_type', "\"${project.rootProject.ext.device_type}\""
+
     }
     productFlavors {
         xiaomi {//自研

+ 1 - 7
android_visiting/build.gradle

@@ -33,14 +33,8 @@ android {
         buildConfigField "String", "BUILD_TIME", getDate()
         buildConfigField 'String', 'VERSION_NAME', "\"${project.rootProject.ext.app_version}\""
         buildConfigField 'String', 'VERSION_CODE', "\"${project.rootProject.ext.app_version_code}\""
-        buildConfigField 'String', 'iscallingdoor', "\"${project.rootProject.ext.callingdoor}\""
-        buildConfigField 'String', 'isandroid_bed', "\"${project.rootProject.ext.android_bed}\""
-        buildConfigField 'String', 'isandroid_host', "\"${project.rootProject.ext.android_host}\""
-        buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
-        buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
-        buildConfigField 'String', 'open_sleep', "\"${project.rootProject.ext.open_sleep}\""
-        buildConfigField 'String', 'open_433', "\"${project.rootProject.ext.open_433}\""
+        buildConfigField 'String', 'device_type', "\"${project.rootProject.ext.device_type}\""
     }
 
     productFlavors {

+ 2 - 0
app/build.gradle

@@ -282,6 +282,8 @@ dependencies {
         compile project(':android_mobile')
     } else if (rootProject.ext.android_visiting.toBoolean()) {
         compile project(':android_visiting')
+    }else if (rootProject.ext.is_agora.toBoolean()) {
+        compile project(':modules_agora')
     }
     /**
      * JavaShopAndroid 中间件依赖库

+ 56 - 0
bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java

@@ -115,6 +115,26 @@ public class SerialPortUtil {
             e.printStackTrace();
         }
     }
+    //大朝华10寸门口机串口
+    public void openSerialPortDchDOOR10() {
+        Log.i(TAG, "打开串口 10寸大朝华");
+        try {
+            serialPort = new SerialPort();
+            serialPort.setCallback(new SerialPort.Callback() {
+                @Override
+                public void onOpen(boolean success) {
+                    //获取打开的串口中的输入输出流,以便于串口数据的收发
+                    inputStream = serialPort.getInputStream();
+                    outputStream = serialPort.getOutputStream();
+                    isOpenSerialPortUtil = true;
+                    receiveSerialPort();
+                }
+            });
+            serialPort.open(new File("/dev/ttyMT1"), 9600, 0);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
 
     //亿莱顿可视分机串口
     public void openSerialPortYld() {
@@ -445,6 +465,12 @@ public class SerialPortUtil {
     public void closeHeart() {
         send("$HEART,WE#");
     }
+    /**
+     * 灯光信号
+     */
+    public void startLight(String command) {
+        sendDch(command);
+    }
 
     /**
      * 系统重启
@@ -505,6 +531,25 @@ public class SerialPortUtil {
         }
     }
 
+    /**
+     * 大朝华门灯
+     * 发送串口数据的方法
+     *
+     * @param command 要发送的数据
+     */
+    private void sendDch(String command) {
+        try {
+            if (isOpenSerialPortUtil) {
+//                byte[] sendData = command.getBytes();
+                outputStream.write(hexStringToByteArray(command));
+                Log.d("serialPort","==command==" + command);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            Log.d("serialPort","==command==" + command);
+        }
+    }
+
     public void setOnDataReceiveListener(ISerialPortBedOnclickEvent dataReceiveListener) {
         onDataReceiveListener = dataReceiveListener;
     }
@@ -662,4 +707,15 @@ public class SerialPortUtil {
     }
 
 
+    public static byte[] hexStringToByteArray(String hexString) {
+        hexString = hexString.replaceAll(" ", "");
+        int len = hexString.length();
+        byte[] bytes = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
+            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
+                    .digit(hexString.charAt(i + 1), 16));
+        }
+        return bytes;
+    }
 }

+ 1 - 6
callingdoor/build.gradle

@@ -36,14 +36,9 @@ android {
         buildConfigField "String", "BUILD_TIME", getDate()
         buildConfigField 'String', 'VERSION_NAME', "\"${project.rootProject.ext.app_version}\""
         buildConfigField 'String', 'VERSION_CODE', "\"${project.rootProject.ext.app_version_code}\""
-        buildConfigField 'String', 'iscallingdoor', "\"${project.rootProject.ext.callingdoor}\""
-        buildConfigField 'String', 'isandroid_bed', "\"${project.rootProject.ext.android_bed}\""
-        buildConfigField 'String', 'isandroid_host', "\"${project.rootProject.ext.android_host}\""
-        buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
-        buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
-        buildConfigField 'String', 'open_sleep', "\"${project.rootProject.ext.open_sleep}\""
         buildConfigField 'String', 'open_433', "\"${project.rootProject.ext.open_433}\""
+        buildConfigField 'String', 'device_type', "\"${project.rootProject.ext.device_type}\""
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     productFlavors {

+ 1 - 1
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/activity/CallingdoorActivationActivity.kt

@@ -101,7 +101,7 @@ class CallingdoorActivationActivity  : BaseActivity<CallingdoorActivationPresent
         XCrashUtils().init(application)
 
 
-        Utils.checkCameraSupport()
+
 
 
         showui()

+ 3 - 1
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/YhFragment.kt

@@ -50,12 +50,14 @@ class  YhFragment : BaseFragment<YhFragmentPresenter, CallingdoorNurseBinding>()
         val metrics = DisplayMetrics()
         activity.windowManager.defaultDisplay.getMetrics(metrics)
         val screenWidth = metrics.widthPixels
-        if(orientation == Configuration.ORIENTATION_PORTRAIT && screenWidth > 600){
+        if(orientation == Configuration.ORIENTATION_PORTRAIT ){
+            //10寸竖屏没问题,15。6寸?
             val layoutManager = GridLayoutManager(activity, 2, LinearLayoutManager.VERTICAL, false)
             rv_nurse_main_view.layoutManager = layoutManager
         }else{
             val layoutManager = GridLayoutManager(activity, 4, LinearLayoutManager.VERTICAL, false)
             rv_nurse_main_view.layoutManager = layoutManager
+
         }
 
 

+ 2 - 1
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/W3288HardTools.java

@@ -16,6 +16,7 @@ import com.wdkl.app.ncs.callingdoor.helper.AppUpdateHelper;
 import com.wdkl.app.ncs.callingdoor.helper.NetHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SOSHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SerialPortHelper;
+import com.wdkl.app.ncs.callingdoor.helper.Utils;
 import com.wdkl.ncs.android.component.welcome.activity.WelcomeActivity;
 import com.wdkl.ncs.android.lib.utils.AppTool;
 import com.wdkl.ncs.android.middleware.common.Constant;
@@ -44,7 +45,7 @@ public class W3288HardTools extends HardTools {
 
     @Override
     public void init() {
-
+        Utils.checkCameraSupport();
     }
 
     @Override

+ 23 - 13
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/WDCHHardTools.java

@@ -6,18 +6,14 @@ import android.app.Application;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.provider.Settings;
-
-import com.example.yf_rk3288_api.YF_RK3288_API_Manager;
 import com.wdkl.app.ncs.callingdoor.hardware.HardTools;
+import com.wdkl.app.ncs.callingdoor.helper.AppUpdateHelper;
 import com.wdkl.app.ncs.callingdoor.helper.NetHelper;
+import com.wdkl.app.ncs.callingdoor.helper.SerialPortHelper;
 import com.wdkl.ncs.android.component.welcome.activity.WelcomeActivity;
-import com.wdkl.ncs.android.lib.utils.AppTool;
 import com.wdkl.ncs.android.middleware.common.Constant;
 
-import static com.umeng.socialize.utils.ContextUtil.getPackageName;
+import serialporttest.utils.SerialPortUtil;
 
 /**
  * Z-3128硬件控制类
@@ -27,9 +23,6 @@ public class WDCHHardTools extends HardTools {
 
     public static final String HARDWDT_SERVICE = "Z3128HardTools";
 
-
-
-
     private static class Z3128HardToolsHolder{
         private final  static WDCHHardTools z3128HardTools = new WDCHHardTools();
     }
@@ -40,13 +33,15 @@ public class WDCHHardTools extends HardTools {
 
     @Override
     public void init() {
-
+        SerialPortUtil.getInstance().openSerialPortDchDOOR10();
     }
 
     @Override
     public void unInit() {
-
+        SerialPortUtil.getInstance().closeSerialPort();
     }
+
+
     @Override
     public void setSOSStart() {
 
@@ -58,7 +53,22 @@ public class WDCHHardTools extends HardTools {
     }
     @Override
     public void setDoorLight(int type) {
-
+        if (type==1){
+            //绿色闪烁
+            SerialPortUtil.getInstance().startLight("fbee0402000a");
+        }else if (type==2){
+            //关闭
+            SerialPortUtil.getInstance().startLight("fbee0002000a");
+        }else if (type==3){
+            //红色闪烁
+            SerialPortUtil.getInstance().startLight("fbee0102000a");
+        }else if (type==4){
+            //关闭
+            SerialPortUtil.getInstance().startLight("fbee0002000a");
+        }else if (type==5){
+            //黄色闪烁
+            SerialPortUtil.getInstance().startLight("fbee0202000a") ;
+        }
     }
     @Override
     public void resetDevice() {

+ 2 - 1
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Wa133HardTools.java

@@ -15,6 +15,7 @@ import com.wdkl.app.ncs.callingdoor.hardware.HardTools;
 import com.wdkl.app.ncs.callingdoor.helper.NetHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SOSHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SerialPortHelper;
+import com.wdkl.app.ncs.callingdoor.helper.Utils;
 import com.wdkl.ncs.android.component.welcome.activity.WelcomeActivity;
 import com.wdkl.ncs.android.lib.base.BaseApplication;
 import com.wdkl.ncs.android.lib.core.locale.SettingConfigNew;
@@ -48,7 +49,7 @@ public class Wa133HardTools extends HardTools {
         if (Boolean.parseBoolean(BuildConfig.open_433)){
             SerialPortUtil433.getInstance().openSerialPort();
         }
-
+        Utils.checkCameraSupport();
     }
 
     @Override

+ 0 - 1
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Z3128HardTools.java

@@ -140,7 +140,6 @@ public class Z3128HardTools extends HardTools {
             //红色
             SerialPortHelper.setDoorLight(1, "200"); //红色闪烁
         }else if (type==4){
-            //红色
             SerialPortHelper.setDoorLight(0, "000"); //关闭
         }
     }

+ 2 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/hardware/imp/Z3368HardTools.java

@@ -12,6 +12,7 @@ import com.wdkl.app.ncs.callingdoor.helper.AppUpdateHelper;
 import com.wdkl.app.ncs.callingdoor.helper.NetHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SOSHelper;
 import com.wdkl.app.ncs.callingdoor.helper.SerialPortHelper;
+import com.wdkl.app.ncs.callingdoor.helper.Utils;
 import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.utils.AppUtil;
 
@@ -43,6 +44,7 @@ public class Z3368HardTools extends HardTools {
         SerialPortUtil.getInstance().openSerialPort();
         //打开433串口0
         SerialPortUtil433.getInstance().openSerialPort();
+        Utils.checkCameraSupport();
     }
 
     @Override

+ 7 - 3
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/DoorLightHelper.java

@@ -1,5 +1,6 @@
 package com.wdkl.app.ncs.callingdoor.helper;
 
+import com.wdkl.app.ncs.callingdoor.hardware.HardWareFactroy;
 import com.wdkl.app.ncs.callingdoor.settings.SettingConfig;
 import com.wdkl.ncs.android.lib.base.BaseApplication;
 import com.wdkl.ncs.android.middleware.common.Constant;
@@ -38,12 +39,15 @@ public class DoorLightHelper {
         //有人入住时点亮门灯,无人入住关闭门灯
         Constant.inNursing = SettingConfig.getInNursing(BaseApplication.appContext);
         if (Constant.inNursing) {
-            SerialPortHelper.setDoorLight(1, Constant.nursingColor); //绿色
+//            SerialPortHelper.setDoorLight(1, Constant.nursingColor); //绿色
+            HardWareFactroy.getHardTools().setDoorLight(1);
         } else {
             if (Constant.bedCustomIn) {
-                SerialPortHelper.setDoorLight(1, "111"); //白色
+//                SerialPortHelper.setDoorLight(1, "111"); //白色
+                HardWareFactroy.getHardTools().setDoorLight(2);
             } else {
-                SerialPortHelper.setDoorLight(0, "000"); //关闭
+                HardWareFactroy.getHardTools().setDoorLight(4);
+//                SerialPortHelper.setDoorLight(0, "000"); //关闭
             }
         }
     }

+ 6 - 5
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/SOSHelper.java

@@ -5,6 +5,7 @@ import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 
+import com.wdkl.app.ncs.callingdoor.hardware.HardWareFactroy;
 import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.tcp.channel.OtherUtil;
 
@@ -24,7 +25,8 @@ public class SOSHelper {
     public static void sosStart() {
             SerialPortHelper.setSosLight("2");
             OtherUtil.sendSosCall(Constant.DEVICE_ID);
-            SerialPortHelper.setDoorLight(1, "200"); //红色闪烁
+            HardWareFactroy.getHardTools().setDoorLight(3);
+//            SerialPortHelper.setDoorLight(1, "200"); //红色闪烁
             Constant.sosOn = true;
             //5min之后紧急按钮灯自动复位
             handler.sendEmptyMessageDelayed(110, 300000);
@@ -36,14 +38,13 @@ public class SOSHelper {
             if (Constant.day_state == 1) {
                 SerialPortHelper.setSosLight("1");
             } else {
-                SerialPortHelper.setSosLight("0");
+                HardWareFactroy.getHardTools().setDoorLight(4);
             }
-
             Constant.sosOn = false;
-
             //如果当前在护理中则不操作门灯,如果不在护理中则重置门灯
             if (Constant.inNursing) {
-                SerialPortHelper.setDoorLight(1, Constant.nursingColor); //绿色
+//                SerialPortHelper.setDoorLight(1, Constant.nursingColor); //绿色
+                HardWareFactroy.getHardTools().setDoorLight(1);
             } else {
                 //SerialPortHelper.setDoorLight(1, "111"); //白色
                 DoorLightHelper.resetDoorLight();

+ 1 - 0
middleware/build.gradle

@@ -27,6 +27,7 @@ android {
         buildConfigField 'String', 'isandroid_mobile', "\"${project.rootProject.ext.android_mobile}\""
         buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
+        buildConfigField 'String', 'is_agora', "\"${project.rootProject.ext.is_agora}\""
     }
 
     lintOptions {

+ 35 - 8
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constant.java

@@ -75,17 +75,20 @@ public class Constant {
     //TCP服务端口
     public static int TCP_PORT = 5060;
 
-    //测试地址床垫服务端口
+//    //测试地址床垫服务端口
+//    public static int SLEEP_TCP_PORT = 5084;
+//
+    //测试地址床垫服务器地址:
+    public static String SLEEP_TCP_SERVER_URL_text = "192.168.1.199";
+
+    //床垫服务端口
     public static int SLEEP_TCP_PORT = 5084;
 
-    //测试地址床垫服务器地址:
-    public static String SLEEP_TCP_SERVER_URL = "192.168.1.199";
+    //探视服务端口
+    public static int VISIT_TCP_PORT = 5085;
 
-//    //床垫服务端口
-//    public static int SLEEP_TCP_PORT = 5084;
-//
-//    //床垫服务器地址:
-//    public static String SLEEP_TCP_SERVER_URL = "47.107.238.130";
+    //床垫/探视服务器地址:
+    public static String SLEEP_TCP_SERVER_URL = "47.107.238.130";
 
     //TCP服务连接心跳
     public static int TCP_HEART_BEAT = 60;
@@ -96,6 +99,9 @@ public class Constant {
     //SleepTCP连接是否成功
     public static boolean Sleep_TCP_CONNECTED = false;
 
+    //探视TCP连接是否成功
+    public static boolean Visit_TCP_CONNECTED = false;
+
     //延时重启app
     public static boolean LATER_RESTART = false;
 
@@ -294,6 +300,7 @@ public class Constant {
     public static boolean  IN_CALL = false;
     public static String  RTC_URL = "";
     public static int  RTC_port = 0;
+    public static String  union_id = "";
 
 
     //广播
@@ -453,6 +460,7 @@ public class Constant {
      */
     public static final int EVENT_REMOVE_CALL_FRAGMENT = 0x09;
 
+
     /**
      * 串口消息
      */
@@ -523,6 +531,10 @@ public class Constant {
     public static final int EVENT_V_br_set = 0x114;
     //退出广播设置
     public static final int EVENT_r_br_set = 0x115;
+    //探视审核进入
+    public static final int EVENT_V_VISIT_th = 0x116;
+    //探视审核退出
+    public static final int EVENT_r_VISIT_th = 0x117;
 
 
     /**
@@ -567,4 +579,19 @@ public class Constant {
      * */
     public static final int EVENT_SLEEP_STOP = 0x209;
 
+    /**
+     * 打开院外探视
+     * */
+    public static final int EVENT_VISIT_START= 0x210;
+
+    /**
+     * 关闭院外探视
+     * */
+    public static final int EVENT_VISIT_STOP= 0x211;
+
+    /**
+     * 院外探视列表刷新
+     * */
+    public static final int EVENT_VISIT_list= 0x212;
+
 }

+ 18 - 2
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/MessageEvent.java

@@ -18,9 +18,12 @@ public class MessageEvent {
 
     private String slepp_data;
     private String slepp_data_lin;
+    private String channelName;
 
-    public MessageEvent(String time_name_1,String time_name_2,String time_name_3,
-                        String time_1,String time_2,String time_3,int type) {
+
+
+    public MessageEvent(String time_name_1, String time_name_2, String time_name_3,
+                        String time_1, String time_2, String time_3, int type) {
         this.time_name_1 = time_name_1;
         this.time_name_2 = time_name_2;
         this.time_name_3 = time_name_3;
@@ -94,10 +97,23 @@ public class MessageEvent {
         this.time_3 = time_3;
     }
 
+    public String getChannelName() {
+        return channelName;
+    }
+
+    public void setChannelName(String channelName) {
+        this.channelName = channelName;
+    }
+
     public MessageEvent(Object message, int type) {
         this.message = message;
         this.type = type;
     }
+    public MessageEvent(Object message, int type,String channelName) {
+        this.message = message;
+        this.type = type;
+        this.channelName = channelName;
+    }
     public MessageEvent(Object message, int type,String slepp_data,String slepp_data_lin) {
         this.message = message;
         this.type = type;

+ 12 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/PartSettingDO.java

@@ -522,7 +522,19 @@ public class PartSettingDO implements Serializable {
     @ApiModelProperty(value="科室的时区",required=false)
     private String time_zone;
 
+    /**
+     *	科室的时区
+     */	@Column(name = "bool_outside_vital" )
+    @ApiModelProperty(value="是否开启院外探视",required=false)
+    private boolean bool_outside_vital;
 
+    public boolean isBool_outside_vital() {
+        return bool_outside_vital;
+    }
+
+    public void setBool_outside_vital(boolean bool_outside_vital) {
+        this.bool_outside_vital = bool_outside_vital;
+    }
 
     @PrimaryKeyField
     public Integer getId() {

文件差异内容过多而无法显示
+ 233 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/VisitPageVo.java


+ 15 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java

@@ -2,6 +2,8 @@ package com.wdkl.ncs.android.middleware.tcp.channel;
 
 import android.util.Log;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.google.gson.Gson;
 import com.wdkl.ncs.android.middleware.BuildConfig;
 import com.wdkl.ncs.android.middleware.R;
@@ -212,6 +214,19 @@ public class DeviceChannel {
                     Constant.phoneDataVO = phoneInteractionVO;
                 }
                 break;
+            case REMOTE_VISIT:
+//                    //不是护士主机
+//                if (Constant.CALL_STATE != Constant.CALL_STANDBY && Boolean.parseBoolean(BuildConfig.isandroid_bed)) {
+//                    JSON json = JSON.parseObject(tcpModel.getData().toString());
+//                    String channelName = ((JSONObject) json).getString("id");
+//                    responseTcpModel = SwVisitUtil.BedvisitIngCall( channelName);
+//                    return responseTcpModel;
+//                } else {
+//                    EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
+//                    return responseTcpModel;
+//                }
+
+                EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
             default:
                 EventBus.getDefault().post(new MessageEvent(tcpModel, 0));
                 break;

+ 18 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceUtil.java

@@ -12,6 +12,9 @@ import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
 import com.wdkl.ncs.android.middleware.utils.CommonUtils;
 import com.wdkl.ncs.android.middleware.utils.ContactHelper;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class DeviceUtil {
 
     public static TcpModel deviceConnect(String mac){
@@ -61,6 +64,21 @@ public class DeviceUtil {
         return tcpModel;
     }
 
+   /*•
+   *
+   * 探视登录
+   * */
+    public static TcpModel deviceConnect4(String unionid, String mac){
+        TcpModel tcpModel = new TcpModel();
+        tcpModel.setType(TcpType.DEVICE);
+        tcpModel.setAction(TcpAction.DeviceAction.CONNECT);
+        Map map = new HashMap<String, String>();
+        map.put("unionId", unionid);
+        map.put("mac", mac);
+        tcpModel.setData(map);
+        return tcpModel;
+    }
+
     public static TcpModel userChange(Integer fromId){
         TcpModel tcpModel = new TcpModel(null);
         tcpModel.setType(TcpType.DEVICE);

+ 128 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/SwVisitUtil.java

@@ -0,0 +1,128 @@
+package com.wdkl.ncs.android.middleware.tcp.channel;
+
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SwVisitUtil {
+
+    //通知分机进入探视
+    //, String bedId         map.put("bed", bedId);
+    public static TcpModel visitCall(Integer fromId, Integer toId,String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.CALL);
+        tcpModel.setFromId(fromId);
+        tcpModel.setToId(toId);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+    //通知分机 小程序挂断
+    public static TcpModel visitHangup(Integer fromId,String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.HANDOFF);
+        tcpModel.setFromId(fromId);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+    //通知分机 小程序取消--直接消失页面不进入探视界面(这里注意,如果分机一直未接听,呼叫系统又有了呼叫,直接发拒绝给小程序)
+    public static TcpModel visitCancel(Integer fromId,String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.CANCEL);
+        tcpModel.setFromId(fromId);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+
+
+    //通知小程序分机不在线去掉tid
+    public static TcpModel visitHandoff(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.FAILED);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+    //通知小程序分机正在通话中
+    public static TcpModel visitIngCall(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.CALLING);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+    //通知小程序分机拒绝
+    public static TcpModel visitreject(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.REJECT);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+    //通知小程序分机挂断
+    public static TcpModel visitWxHANDOFF(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.HANDOFF);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+   //  护士主机挂断 发给分机
+    public static TcpModel visitHostHangup(Integer fromId,String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setFromId(fromId);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.HANDOFF);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+    //发给小程序
+    public static TcpModel visitHostHangup2(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.HANDOFF);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+    //分机通知主机
+
+    //通知护士主机正在通话中
+    public static TcpModel BedvisitIngCall(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.CALLING);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+    //通知护士主机挂断
+    public static TcpModel BedvisitHangup(Integer fromId,String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.HANDOFF);
+        tcpModel.setFromId(fromId);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+    //通知护士主机拒绝
+    public static TcpModel BedvisitRemote(String visitid){
+        TcpModel tcpModel = new TcpModel(null);
+        tcpModel.setType(TcpType.REMOTE_VISIT);
+        tcpModel.setAction(TcpAction.REMOTEVISITAction.REJECT);
+        tcpModel.setData(visitid);
+        return tcpModel;
+    }
+
+}

+ 7 - 1
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/dto/TcpModel.java

@@ -3,6 +3,7 @@ package com.wdkl.ncs.android.middleware.tcp.dto;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.base.Strings;
+import com.wdkl.ncs.android.middleware.BuildConfig;
 import com.wdkl.ncs.android.middleware.model.dos.DeviceDO;
 import com.wdkl.ncs.android.middleware.model.dos.FrameDO;
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
@@ -15,6 +16,8 @@ import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
 
+import static com.wdkl.ncs.android.middleware.BuildConfig.is_agora;
+
 /**
  * tcp传输对象
  *
@@ -51,7 +54,7 @@ public class TcpModel implements Serializable {
     }
 
     public TcpModel(String tid){
-        if (Strings.isNullOrEmpty(tid)) {
+        if (Strings.isNullOrEmpty(tid) ) {
             this.tid = new ObjectId().toString();
         } else {
             this.tid = tid;
@@ -190,6 +193,9 @@ public class TcpModel implements Serializable {
             case PHONE:
                 tcpAction = TcpAction.PhoneAction.fromString(action);
                 break;
+            case REMOTE_VISIT:
+                tcpAction = TcpAction.REMOTEVISITAction.fromString(action);
+                break;
         }
 
         tcpModel.setTid(tid);

+ 33 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpAction.java

@@ -836,5 +836,38 @@ public interface TcpAction {
             return ENUM_MAP.get(v);
         }
     }
+    enum REMOTEVISITAction implements TcpAction {
+        CALL("呼叫"),
+        VOICE_OFF("确认进入"),
+        REJECT("拒绝"),
+        HANDOFF("挂断"),
+        FAILED("分机不在线"),
+        DATA("刷新预约列表"),
+        CALLING("正在通话中"),
+        CANCEL("取消");
+
+
+        private final String description;
+        REMOTEVISITAction(String description){
+            this.description = description;
+        }
+        public String getDescription() {
+            return description;
+        }
+
+        public String getName(){
+            return this.name();
+        }
+
+        private final static Map<String , REMOTEVISITAction> ENUM_MAP = new HashMap<String, REMOTEVISITAction>();
+        static {
+            for(REMOTEVISITAction v : values()) {
+                ENUM_MAP.put(v.toString() , v);
+            }
+        }
+        public static REMOTEVISITAction fromString(String v) {
+            return ENUM_MAP.get(v);
+        }
+    }
 
 }

+ 3 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpType.java

@@ -31,8 +31,11 @@ public enum TcpType {
     ROOMCHECK("床旁交互巡房"),
     SCREEN_TIP("设置门口机提示语"),
     AUTH("认证"),
+    //声网探视
+    REMOTE_VISIT("院外探视"),
     BLUE_CODE("blue code");
 
+
     private final String description;
     TcpType(String description){
         this.description = description;

android_host/src/main/res/drawable/shape_bt_call_bg.xml → resource/src/main/res/drawable/shape_bt_call_bg.xml


+ 8 - 0
resource/src/main/res/drawable/shape_bt_main_add_bg.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#3A78EF"/>
+    <corners
+        android:radius="10dp"
+      />
+
+</shape>

android_host/src/main/res/drawable/shape_bt_main_hh_bg.xml → resource/src/main/res/drawable/shape_bt_main_hh_bg.xml


android_host/src/main/res/drawable/shape_bt_main_list_bg.xml → resource/src/main/res/drawable/shape_bt_main_list_bg.xml


android_host/src/main/res/drawable/shape_other_item_bg.xml → resource/src/main/res/drawable/shape_other_item_bg.xml


android_host/src/main/res/mipmap-xhdpi/jian_bai.png → resource/src/main/res/mipmap-xhdpi/jian_bai.png


android_host/src/main/res/mipmap-xhdpi/jr.png → resource/src/main/res/mipmap-xhdpi/jr.png


android_host/src/main/res/mipmap-xhdpi/shan.png → resource/src/main/res/mipmap-xhdpi/shan.png


+ 1 - 0
welcome/build.gradle

@@ -38,6 +38,7 @@ android {
         buildConfigField 'String', 'isandroid_visiting', "\"${project.rootProject.ext.android_visiting}\""
         buildConfigField 'String', 'is_mom', "\"${project.rootProject.ext.is_mom}\""
         buildConfigField 'String', 'device_type', "\"${project.rootProject.ext.device_type}\""
+        buildConfigField 'String', 'is_agora', "\"${project.rootProject.ext.is_agora}\""
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 
     }