Просмотр исходного кода

主机和分机增加离线呼叫模式

weizhengliang 8 месяцев назад
Родитель
Сommit
d02b0f9084
67 измененных файлов с 6128 добавлено и 540 удалено
  1. 7 0
      android_bed/src/main/AndroidManifest.xml
  2. 66 51
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingActivity.kt
  3. 52 29
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivationActivity.kt
  4. 165 4
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivity.kt
  5. 1193 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/OfflineCallingbedActivity.kt
  6. 4 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/di/CallingbedComponent.kt
  7. 25 1
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/MainFragment.kt
  8. 7 2
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/NursingWorkFragment.kt
  9. 496 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/OfflineCallFragment.kt
  10. 153 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/OfflineMainFragment.kt
  11. 28 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/helper/SOSHelper.java
  12. 22 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/settings/SettingConfig.java
  13. 29 0
      android_bed/src/main/java/com/wdkl/app/ncs/callingbed/sip/core/LinphoneManager.kt
  14. 96 0
      android_bed/src/main/res/layout-land/callingbed_offline_main_new.xml
  15. 97 0
      android_bed/src/main/res/layout/callingbed_offline_main_new.xml
  16. 387 0
      android_bed/src/main/res/layout/main_view_offline_layout.xml
  17. 1 1
      android_bed/src/main/res/layout/view_title_layout.xml
  18. 10 0
      android_bed/src/main/sharedUserId/AndroidManifest.xml
  19. 5 0
      android_host/src/main/AndroidManifest.xml
  20. 66 3
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/CallingHostActivationActivity.kt
  21. 1 2
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/CreateMessageActivity.kt
  22. 134 28
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt
  23. 1095 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/OfflineHomeActivity.kt
  24. 1 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/SystemActivity.kt
  25. 125 39
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/CallingItemAdapter.kt
  26. 121 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/OfflineFrameBedAdapter.kt
  27. 3 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/di/NurseHomeComponent.kt
  28. 0 2
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/BaseCallFragment.kt
  29. 93 12
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/CallRecordsFragment.kt
  30. 39 2
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/FramePartFragment.kt
  31. 1 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/IotDeviceFragment.kt
  32. 1 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/MessageFragment.kt
  33. 205 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineBedFragment.kt
  34. 386 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineCallFragment.kt
  35. 302 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineCallRecordsFragment.kt
  36. 2 2
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/SipCallFragment.kt
  37. 37 23
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/WorkFragment.kt
  38. 1 3
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/hardware/imp/Z3128HardTools.java
  39. 0 1
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/hardware/imp/ZKEHardTools.java
  40. 39 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/settingconfig/SettingConfig.java
  41. 29 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/sip/core/LinphoneManager.kt
  42. 1 9
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/window/IncidentWindow.kt
  43. 222 0
      android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/window/IncidentWindow2.kt
  44. 1 0
      android_host/src/main/res/layout/adapter_calling_item.xml
  45. 55 0
      android_host/src/main/res/layout/adapter_offline_home_frame_bed_lay.xml
  46. 1 1
      android_host/src/main/res/layout/view_title_layout.xml
  47. 1 1
      android_host/src/main/res/values/strings.xml
  48. 1 1
      middleware/build.gradle
  49. 2 2
      middleware/src/main/code/com/wdkl/greendao/gen/DaoMaster.java
  50. 56 28
      middleware/src/main/code/com/wdkl/greendao/gen/FrameBedBeanDao.java
  51. 8 1
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constant.java
  52. 35 11
      middleware/src/main/code/com/wdkl/ncs/android/middleware/dao/entity/FrameBedBean.java
  53. 36 2
      middleware/src/main/code/com/wdkl/ncs/android/middleware/entity/CallingItem.java
  54. 10 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/NurseDeviceInfoVO.java
  55. 1 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpHelper.java
  56. 4 2
      middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpIndex.java
  57. 87 36
      middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpItem.java
  58. 27 11
      middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpReceivedUtil.java
  59. 30 14
      middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpSendUtil.java
  60. 0 169
      middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/AudioRouteUtils.kt
  61. 1 1
      middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/CommonUtils.java
  62. 0 41
      middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/MessageEvent.kt
  63. 1 0
      resource/src/main/res/values-es/strings.xml
  64. 13 1
      resource/src/main/res/values-pt/strings.xml
  65. 1 0
      resource/src/main/res/values-ru/strings.xml
  66. 9 0
      resource/src/main/res/values-zh/strings.xml
  67. 1 0
      resource/src/main/res/values/strings.xml

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

@@ -77,6 +77,13 @@
                 android:name="android.nfc.action.TECH_DISCOVERED"
                 android:resource="@xml/nfc_tech" />
         </activity>
+
+        <activity android:name="com.wdkl.app.ncs.callingbed.activity.OfflineCallingbedActivity"
+            android:turnScreenOn="true"
+            android:screenOrientation="nosensor"
+            android:launchMode="singleTask" >
+        </activity>
+
         <activity android:name="com.wdkl.app.ncs.callingbed.activity.CallingbedActivationActivity"
             android:screenOrientation="nosensor"
             android:launchMode="singleTask"/>

+ 66 - 51
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingActivity.kt

@@ -14,6 +14,7 @@ import com.wdkl.app.ncs.callingbed.R
 import com.wdkl.app.ncs.callingbed.fragment.*
 import com.wdkl.ncs.android.lib.core.locale.LocaleMangerUtils
 import com.wdkl.ncs.android.lib.core.locale.SettingConfigNew
+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.tcp.dto.TcpModel
@@ -59,60 +60,74 @@ class CallingActivity: BaseToolActivity() {
             tcpModel = intent.getSerializableExtra(Constant.tcp_model) as TcpModel
         }
 
-        if (BuildConfig.device_type.equals("4")) {
-            if (callType == 1) {
-                //sip通话
-                val fragment = DormSipCallFragment()
-                fragment.callState = callState
-                fragment.tcpModel = tcpModel
-                fragment.tid = tid
-                fragment.roleId = roleId
-                fragment.roleType = roleType
-                fragment.callDeviceId = deviceId
-
-                switchFragment(R.id.frame_calling_view, fragment, "Sip_fragment")
+        val callingOffline = intent.getBooleanExtra(Constant.calling_offline, false)
+
+        if (Constant.TCP_CONNECTED) {
+            //与服务器正常连接
+            if (BuildConfig.device_type.equals("4")) {
+                if (callType == 1) {
+                    //sip通话
+                    val fragment = DormSipCallFragment()
+                    fragment.callState = callState
+                    fragment.tcpModel = tcpModel
+                    fragment.tid = tid
+                    fragment.roleId = roleId
+                    fragment.roleType = roleType
+                    fragment.callDeviceId = deviceId
+
+                    switchFragment(R.id.frame_calling_view, fragment, "Sip_fragment")
+                } else {
+                    //默认webrtc通话
+                    val fragment = DormSkyCallFragment()
+                    fragment.callState = callState
+                    fragment.bcPlay = bcPlay
+                    fragment.tcpModel = tcpModel
+                    fragment.tid = tid
+                    fragment.roleId = roleId
+                    fragment.roleType = roleType
+                    fragment.callDeviceId = deviceId
+
+                    switchFragment(R.id.frame_calling_view, fragment, "webrtc_fragment")
+                }
             } else {
-                //默认webrtc通话
-                val fragment = DormSkyCallFragment()
-                fragment.callState = callState
-                fragment.bcPlay = bcPlay
-                fragment.tcpModel = tcpModel
-                fragment.tid = tid
-                fragment.roleId = roleId
-                fragment.roleType = roleType
-                fragment.callDeviceId = deviceId
-
-                switchFragment(R.id.frame_calling_view, fragment, "webrtc_fragment")
+                if (callType == 1) {
+                    //sip通话
+                    val fragment = SipCallFragment()
+                    fragment.callState = callState
+                    fragment.tcpModel = tcpModel
+                    fragment.tid = tid
+                    fragment.roleId = roleId
+                    fragment.roleType = roleType
+
+                    switchFragment(R.id.frame_calling_view, fragment, "Sip_fragment")
+                } else if (callType == 2) {
+                    //远程探视
+                    val fragment = VisitCallFragment()
+                    fragment.channelName = channelName
+
+                    switchFragment(R.id.frame_calling_view, fragment, "remote_fragment")
+                } else {
+                    //默认webrtc通话
+                    val fragment = SkyCallFragment()
+                    fragment.callState = callState
+                    fragment.bcPlay = bcPlay
+                    fragment.tcpModel = tcpModel
+                    fragment.tid = tid
+                    fragment.roleId = roleId
+                    fragment.roleType = roleType
+
+                    switchFragment(R.id.frame_calling_view, fragment, "webrtc_fragment")
+                }
             }
+        } else if (callingOffline) {
+            //设备有网,但是与服务器断开连接,使用udp离线呼叫
+            val fragment = OfflineCallFragment()
+            fragment.callState = callState
+
+            switchFragment(R.id.frame_calling_view, fragment, "OfflineCall_fragment")
         } else {
-            if (callType == 1) {
-                //sip通话
-                val fragment = SipCallFragment()
-                fragment.callState = callState
-                fragment.tcpModel = tcpModel
-                fragment.tid = tid
-                fragment.roleId = roleId
-                fragment.roleType = roleType
-
-                switchFragment(R.id.frame_calling_view, fragment, "Sip_fragment")
-            } else if (callType == 2) {
-                //远程探视
-                val fragment = VisitCallFragment()
-                fragment.channelName = channelName
-
-                switchFragment(R.id.frame_calling_view, fragment, "remote_fragment")
-            } else {
-                //默认webrtc通话
-                val fragment = SkyCallFragment()
-                fragment.callState = callState
-                fragment.bcPlay = bcPlay
-                fragment.tcpModel = tcpModel
-                fragment.tid = tid
-                fragment.roleId = roleId
-                fragment.roleType = roleType
-
-                switchFragment(R.id.frame_calling_view, fragment, "webrtc_fragment")
-            }
+            showMessage(R.string.net_error)
+            finish()
         }
     }
 

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

@@ -31,6 +31,7 @@ import com.wdkl.app.ncs.callingbed.settings.SettingConfig
 import com.wdkl.app.ncs.callingbed.utils.SPUtils
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.utils.AppTool
 import com.wdkl.ncs.android.lib.utils.EcodeHelper
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.lib.vo.filter
@@ -66,13 +67,14 @@ class CallingbedActivationActivity  : BaseActivity<CallingbedActivationPresenter
     private var serverSuccess = false
     //是否重启
     private var cancelRestart = false
+    private var offlineLoad = false
 
     private var clickTime: Long = 0
     private var clickTimes: Int = 1
 
     private val REQUEST_MANAGER_PERMISSION = 11
 
-     var viewService: ViewService? = null
+    var viewService: ViewService? = null
 
     override fun getLayId(): Int {
 
@@ -118,6 +120,7 @@ class CallingbedActivationActivity  : BaseActivity<CallingbedActivationPresenter
 
         if (!Settings.System.canWrite(this)) {
             Utils.hideStatusBar(activity, false)
+
             val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
             intent.data = Uri.parse("package:" + this.packageName)
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -328,49 +331,69 @@ class CallingbedActivationActivity  : BaseActivity<CallingbedActivationPresenter
 
 
     private fun checkServer() {
-        Thread {
-            while (!serverSuccess) {
-                val okHttpClient = OkHttpClient().newBuilder()
+        if (SettingConfig.getOfflineModeRunning(activity)) {
+            Log.e(TAG, "goto offline mode...")
+            if (!TextUtils.isEmpty(NetHelper.getInstance().localIP)) {
+                //进入离线模式
+                AppTool.Time.delay(5000) {
+                    if (!offlineLoad) {
+                        offlineLoad = true
+
+                        val intent = Intent()
+                        intent.setClass(activity, OfflineCallingbedActivity::class.java)
+                        activity.startActivity(intent)
+
+                        finish()
+                    }
+                }
+            } else {
+                Log.e(TAG, "没有IP地址")
+            }
+        } else {
+            Thread {
+                while (!serverSuccess) {
+                    val okHttpClient = OkHttpClient().newBuilder()
                         .connectTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
                         .readTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
                         .writeTimeout(15 * 1000L, TimeUnit.MILLISECONDS)
                         .build()
-                val url: String = CommonUtils.getUrl(BaseApplication.appContext)
-                val port: String = CommonUtils.getUrlPort(BaseApplication.appContext)
-                val request = Request.Builder()
+                    val url: String = CommonUtils.getUrl(BaseApplication.appContext)
+                    val port: String = CommonUtils.getUrlPort(BaseApplication.appContext)
+                    val request = Request.Builder()
                         .url("http://$url:$port/ncs_url/server_info")
                         .get()
                         .build()
 
-                try {
-                    Log.i(TAG, "start check server: $url,$port, ${Constant.DEVICE_REGISTER_ID}")
-                    val response = okHttpClient.newCall(request).execute()
-                    Log.i(TAG, "server check end: ")
-                    if (response != null && response.isSuccessful) {
-                        //接口数据获取成功,检查设备是否已注册
-                        if (!TextUtils.isEmpty(Constant.DEVICE_REGISTER_ID)) {
-                            presenter.getDeviceInfo(Constant.DEVICE_REGISTER_ID)
+                    try {
+                        Log.i(TAG, "start check server: $url,$port, ${Constant.DEVICE_REGISTER_ID}")
+                        val response = okHttpClient.newCall(request).execute()
+                        Log.i(TAG, "server check end: ")
+                        if (response != null && response.isSuccessful) {
+                            //接口数据获取成功,检查设备是否已注册
+                            if (!TextUtils.isEmpty(Constant.DEVICE_REGISTER_ID)) {
+                                presenter.getDeviceInfo(Constant.DEVICE_REGISTER_ID)
+                            }
+                        } else {
+                            //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                            val info = ServerInfoUtil.get(Constant.DEVICE_REGISTER_ID!!)
+                            checkServerInfo(info)
                         }
-                    } else {
+                    } catch (e: Exception) {
                         //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
+                        Log.e(TAG, "check server exception:")
+                        e.printStackTrace()
                         val info = ServerInfoUtil.get(Constant.DEVICE_REGISTER_ID!!)
                         checkServerInfo(info)
                     }
-                } catch (e: Exception) {
-                    //接口数据获取失败,可能服务器ip不对,尝试重新获取服务器ip
-                    Log.e(TAG, "check server exception:")
-                    e.printStackTrace()
-                    val info = ServerInfoUtil.get(Constant.DEVICE_REGISTER_ID!!)
-                    checkServerInfo(info)
-                }
 
-                try {
-                    Thread.sleep(45000)
-                } catch (ex: Exception) {
-                    ex.printStackTrace()
+                    try {
+                        Thread.sleep(45000)
+                    } catch (ex: Exception) {
+                        ex.printStackTrace()
+                    }
                 }
-            }
-        }.start()
+            }.start()
+        }
     }
 
 

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

@@ -7,6 +7,7 @@ import android.content.pm.PackageManager
 import android.graphics.Color
 import android.media.AudioManager
 import android.net.ConnectivityManager
+import android.net.wifi.WifiManager
 import android.nfc.NfcAdapter
 import android.nfc.Tag
 import android.os.*
@@ -62,6 +63,8 @@ import com.wdkl.ncs.android.middleware.bean.Trans433Data
 import com.wdkl.ncs.android.middleware.common.Constant
 import com.wdkl.ncs.android.middleware.common.MessageEvent
 import com.wdkl.ncs.android.middleware.common.SipStatus
+import com.wdkl.ncs.android.middleware.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.DeviceInfoBean
 import com.wdkl.ncs.android.middleware.entity.CallingItem
 import com.wdkl.ncs.android.middleware.logic.contract.callingbed.BedCallingbedActivityContract
 import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.BedCallingbedActivityPresenter
@@ -78,6 +81,10 @@ 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
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.middleware.udp2.UdpHelper
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import com.wdkl.ncs.android.middleware.utils.RingPlayHelper
 import com.wdkl.ncs.android.middleware.utils.StringUtil
@@ -93,6 +100,7 @@ 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.Call
 import org.linphone.core.TransportType
 import serialporttest.utils.SerialPortUtil
@@ -102,6 +110,9 @@ import java.io.File
 import java.io.FileOutputStream
 import java.io.InputStream
 import java.util.*
+import java.util.concurrent.Executors
+import java.util.concurrent.ScheduledExecutorService
+import java.util.concurrent.TimeUnit
 import kotlin.collections.ArrayList
 import kotlin.concurrent.timerTask
 
@@ -189,6 +200,14 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
     var timer: Timer? = null
     var timerTask: TimerTask? = null
 
+    //udp
+    var helper: UdpHelper? = null
+    var wifiManager: WifiManager? = null
+    private var udpRunning = false
+
+    private var executor: ScheduledExecutorService? = null
+    private var disconnectTimes = 0
+
     private var linphoneManager: LinphoneManager? = null
     private var sipReg: Boolean = false
 
@@ -371,8 +390,80 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                 }
             }.start()
         }
+
+        startScheduledExecutor()
     }
 
+    private fun initUdp() {
+        if (!udpRunning) {
+            wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
+            helper = UdpHelper(wifiManager, applicationContext)
+            helper?.run()
+
+            udpRunning = true
+        }
+    }
+
+    //定时检测任务
+    private fun startScheduledExecutor() {
+        val timerTask: TimerTask = object : TimerTask() {
+            override fun run() {
+                try {
+                    scheduleTask()
+                } catch (e: Exception) {
+                    //
+                }
+            }
+        }
+
+        executor = Executors.newSingleThreadScheduledExecutor()
+        executor!!.scheduleAtFixedRate(timerTask, 40, 30, TimeUnit.SECONDS)
+    }
+
+    private fun scheduleTask() {
+        if (SettingConfig.getOfflineMode(activity)) {
+            if (Constant.TCP_CONNECTED) {
+                disconnectTimes = 0
+            } else {
+                disconnectTimes++
+            }
+
+            Log.e(TAG, "disconnect times: " + disconnectTimes)
+            if (disconnectTimes >= 3) {
+                disconnectTimes = 3
+                //发送设备离线udp通知护士主机
+                UdpSendUtil.getInstance().sendUdpData(
+                    UdpIndex.DEVICE_OFFLINE,
+                    Constant.BED_NAME,
+                    "",
+                    Constant.FJ_NAME,
+                    Constant.DEVICE_REGISTER_ID,
+                    "",
+                    "",
+                    "",
+                    "",
+                    "",
+                    "",
+                    Constant.DEVICE_ID,
+                    -1,
+                    Constant.PART_ID
+                )
+            }
+        }
+
+        runOnUiThread {
+            updateNetState()
+            if (initialized) {
+                updateSettings(false)
+                updateTcpState()
+                if (Constant.LATER_RESTART && Constant.CALL_STATE == Constant.CALL_STANDBY) {
+                    Log.e(TAG, "处于延时重启,并且待机状态")
+                    AppUpdateHelper.restartApp(activity)
+                }
+            }
+            checkNet()
+        }
+    }
 
 
     private fun showMsgMain(msg: String) {
@@ -843,6 +934,7 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
             timerTask!!.cancel()
         }
 
+        DaoManager.getInstance().closeConnection()
     }
 
     //数据加载错误
@@ -908,7 +1000,10 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
         if (deviceInfo.status != null) {
             Constant.DEVICE_STATUS = deviceInfo.status
         }
+
+        Constant.partDisplay = deviceInfo.partDisplay
         view_title_layout_tv_hospital_name.text = deviceInfo.partDisplay
+        view_title_layout_tv_hospital_name.setTextColor(Color.WHITE)
 
         if (deviceInfo.partName != null) {
             Constant.PART_NAME = deviceInfo.partName
@@ -963,6 +1058,27 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
         presenter.loadPartSettings(Constant.PART_ID)
 
         main_rl_3.isEnabled = true
+
+        if (SettingConfig.getOfflineMode(activity)) {
+            //保存本机设备信息到数据库
+            DaoManager.getInstance().asyncSession.runInTx {
+                Log.d(TAG, "save device info to db")
+                DaoManager.getInstance().daoSession.deviceInfoBeanDao.deleteAll()
+                val bean = DeviceInfoBean()
+                bean.hospitalId = deviceInfo.hospitalId
+                bean.hospitalName = deviceInfo.hospitalName
+                bean.id = deviceInfo.id
+                bean.partId = deviceInfo.partId
+                bean.partName = deviceInfo.partName
+                bean.partDisplay = deviceInfo.partDisplay
+                bean.deviceType = deviceInfo.deviceType
+                bean.frameName = deviceInfo.fullName
+                bean.name = deviceInfo.name
+                bean.sipId = deviceInfo.sipId
+                bean.sipPassword = deviceInfo.sipPassword
+                DaoManager.getInstance().daoSession.deviceInfoBeanDao.insert(bean)
+            }
+        }
     }
 
     override fun showCustomInfo(customInfo: CustomerInfoVO) {
@@ -1026,6 +1142,10 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
             }
             HardWareFactory.getHardTools().setTime(this, 0, partSetting.time_zone)
 
+            //todo test: 是否开启离线模式功能
+            //SettingConfig.setOfflineMode(activity, true)
+            //initUdp()
+
             //通过服务端设置语言
             if (SettingConfigNew.getLanguageMode(activity) == 0) {
                 var needReboot = false
@@ -1249,8 +1369,8 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
 
     private fun regReceiver() {
         receiver = TimeReceiver()
-        var intentFilter = IntentFilter()
-        intentFilter.addAction(Intent.ACTION_TIME_TICK)
+        val intentFilter = IntentFilter()
+        //intentFilter.addAction(Intent.ACTION_TIME_TICK)
         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
         intentFilter.addAction(Constant.KEY_CALL_UP)
         intentFilter.addAction(Constant.KEY_HOME_UP)
@@ -1365,6 +1485,10 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                     clickSosTime = System.currentTimeMillis()
                     if (Constant.TCP_CONNECTED) {
                         SOSHelper.sosStart()
+                    } else {
+                        if (SettingConfig.getOfflineMode(BaseApplication.appContext)) {
+                            SOSHelper.sosUdpStart()
+                        }
                     }
                 }
             }
@@ -1481,6 +1605,15 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                     startCallActivity(intent)
                 }
+            } else if (!Constant.TCP_CONNECTED && !TextUtils.isEmpty(Constant.SIP_ID) && !TextUtils.isEmpty(NetHelper.getInstance().localIP)) {
+                //设备有网,但是与服务器断开连接,使用udp离线呼叫
+                val intent = Intent(activity, CallingActivity::class.java)
+                intent.putExtra(Constant.calling_type, 0)
+                intent.putExtra(Constant.calling_state, 0)
+                intent.putExtra(Constant.calling_offline, true)
+                intent.putExtra(Constant.bc_play, broadcastOn)
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                startCallActivity(intent)
             } else {
                 showMessage(R.string.call_init_error)
             }
@@ -2120,6 +2253,28 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                     }
                 }
             }
+
+            //离线状态udp消息
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+                    if (UdpIndex.SERVER_MODE_OFFLINE == udpItem.index) {
+                        Util.wakeUpAndUnlock()
+
+                        //切换离线模式
+                        SettingConfig.setOfflineModeRunning(activity, true)
+
+                        AppTool.Time.delay(5000) {
+                            AppUpdateHelper.restartApp(activity)
+                        }
+                    } else if (UdpIndex.SOS_CANCEL == udpItem.index) {
+                        Util.wakeUpAndUnlock()
+                        //响应紧急呼叫
+                        SOSHelper.sosStop()
+                    }
+                }
+            }
         }
     }
 
@@ -2356,10 +2511,16 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
             view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_success)
             //view_title_layout_tv_point.setBackgroundResource(R.mipmap.sip_b)
             view_title_layout_tv_sip.setTextColor(Color.DKGRAY)
+
+            view_title_layout_tv_hospital_name.text = Constant.partDisplay
+            view_title_layout_tv_hospital_name.setTextColor(Color.WHITE)
         } else {
             view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_nor)
             //view_title_layout_tv_point.setBackgroundResource(R.mipmap.sip_h)
             view_title_layout_tv_sip.setTextColor(Color.RED)
+
+            view_title_layout_tv_hospital_name.setText(R.string.disconnect_server)
+            view_title_layout_tv_hospital_name.setTextColor(Color.parseColor("#FFFD3B30"))
         }
     }
 
@@ -2436,7 +2597,7 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
     //广播
     inner class TimeReceiver: BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
-            if (intent.action == Intent.ACTION_TIME_TICK) {
+            /*if (intent.action == Intent.ACTION_TIME_TICK) {
                 updateNetState()
                 if (initialized) {
                     updateSettings(false)
@@ -2447,7 +2608,7 @@ class CallingbedActivity :BaseActivity<BedCallingbedActivityPresenter, Callingbe
                     }
                 }
                 checkNet()
-            } else if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
+            } else */if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
                 updateNetState()
             } else if (intent.action == Constant.KEY_CALL_UP) {
                 val time = System.currentTimeMillis()

Разница между файлами не показана из-за своего большого размера
+ 1193 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/activity/OfflineCallingbedActivity.kt


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

@@ -59,6 +59,8 @@ interface CallingbedComponent {
 
     fun inject(activity: PushMessageActivity)
 
+    fun inject(activity: OfflineCallingbedActivity)
+
 
     fun inject(fragment: MainFragment)
 
@@ -96,6 +98,8 @@ interface CallingbedComponent {
 
     fun inject(fragment: VisitCallFragment)
 
+    fun inject(fragment: OfflineMainFragment)
+
     //宿舍
     fun inject(activity: CallingbedDormitoryActivity)
 

+ 25 - 1
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/fragment/MainFragment.kt

@@ -1,6 +1,7 @@
 package com.wdkl.app.ncs.callingbed.fragment
 
 import android.text.TextUtils
+import android.util.Log
 import android.view.View
 import androidx.recyclerview.widget.GridLayoutManager
 import com.enation.javashop.net.engine.model.NetState
@@ -10,11 +11,14 @@ import com.wdkl.app.ncs.callingbed.adapter.NurseConfigAdpter
 import com.wdkl.app.ncs.callingbed.databinding.MainViewLayoutBinding
 import com.wdkl.app.ncs.callingbed.helper.Utils
 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.*
 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.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.FrameBedBean
 import com.wdkl.ncs.android.middleware.logic.contract.callingbed.BedMainFragmentContract
 import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.BedMainFragmentPresenter
 import com.wdkl.ncs.android.middleware.model.dos.EventDO
@@ -23,7 +27,6 @@ import com.wdkl.ncs.android.middleware.model.vo.CustomerInfoVO
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import com.wdkl.ncs.android.middleware.utils.StringUtil
 import kotlinx.android.synthetic.main.main_view_layout.*
-import kotlinx.android.synthetic.main.view_bed_name.tv_bed_name
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
@@ -54,6 +57,10 @@ class  MainFragment: BaseFragment<BedMainFragmentPresenter, MainViewLayoutBindin
     override fun init() {
         val layoutManager = GridLayoutManager(getActivity(), 3)
         rv_main_view.setLayoutManager(layoutManager)
+
+        //删除本地原来的入住信息
+        //DaoManager.getInstance().daoSession.frameBedBeanDao.deleteAll()
+
         updateInfo()
     }
 
@@ -154,6 +161,23 @@ class  MainFragment: BaseFragment<BedMainFragmentPresenter, MainViewLayoutBindin
             rv_main_view_ll.visibility =View.GONE
             rv_main_view_s.visibility =View.VISIBLE
         }
+
+        if (SettingConfig.getOfflineMode(activity)) {
+            //保存入住信息到数据库
+            DaoManager.getInstance().asyncSession.runInTx {
+                //删除本地原来的入住信息
+                DaoManager.getInstance().daoSession.frameBedBeanDao.deleteAll()
+                Log.d(TAG, "save customer info to db")
+                val customer = FrameBedBean()
+                customer.id = customInfo.id
+                customer.partId = customInfo.partId
+                customer.customerName = customInfo.named
+                customer.frameName = Constant.BED_NAME
+                customer.doctorName = customInfo.doctorName
+                customer.nurseName = customInfo.nurseName
+                DaoManager.getInstance().daoSession.frameBedBeanDao.insert(customer)
+            }
+        }
     }
 
     override fun showEvents(data: ArrayList<EventDO>) {

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

@@ -60,8 +60,13 @@ class NursingWorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, Nursin
         deviceMenuAdapter.setOnItemClickListener(this)
         recycler.adapter = deviceMenuAdapter
 
-        if (StringUtil.notEmpty(Constant.DEVICE_REGISTER_ID)){
-            presenter.DeviceMenulist(Constant.DEVICE_REGISTER_ID)
+        //离线模式只显示设备信息菜单
+        if (SettingConfig.getOfflineModeRunning(activity)) {
+            showDefaultDeviceMenu()
+        } else {
+            if (StringUtil.notEmpty(Constant.DEVICE_REGISTER_ID)) {
+                presenter.DeviceMenulist(Constant.DEVICE_REGISTER_ID)
+            }
         }
     }
     //点击事件

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

@@ -0,0 +1,496 @@
+package com.wdkl.app.ncs.callingbed.fragment
+
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import android.widget.SeekBar
+import com.alibaba.fastjson.JSONObject
+import com.google.gson.Gson
+import com.wdkl.app.ncs.callingbed.R
+import com.wdkl.app.ncs.callingbed.helper.NetHelper
+import com.wdkl.app.ncs.callingbed.helper.SerialPortHelper
+import com.wdkl.app.ncs.callingbed.helper.VoiceManagerUtil
+import com.wdkl.app.ncs.callingbed.settings.SettingConfig
+import com.wdkl.app.ncs.callingbed.sip.core.LinphoneManager
+import com.wdkl.ncs.android.lib.base.BaseApplication
+
+import com.wdkl.ncs.android.lib.utils.AppTool
+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.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
+import com.wdkl.ncs.android.middleware.utils.RingPlayHelper
+
+import kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import kotlinx.android.synthetic.main.view_title_layout.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class OfflineCallFragment: BaseCallFragment() {
+    private val TAG = "OfflineCallFragment"
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    private var callEnded: Boolean = false
+    private var outGoing: Boolean = false
+
+    private var volume = 60
+
+    private var linphoneManager: LinphoneManager? = null
+
+    private val localIp = NetHelper.getInstance().localIP
+
+    override fun getLayId(): Int {
+        return R.layout.sky_voice_call_layout
+    }
+
+    override fun init() {
+        showUI()
+
+        //初始化计时器
+        initCountDownTimer(sky_voice_call_timeout)
+
+        linphoneManager = LinphoneManager.getInstance(BaseApplication.appContext)
+
+        volume = SettingConfig.getExtensionCallVolume(activity)
+        if (volume < 0 || volume > 100) {
+            volume = 60
+        }
+        call_volume_bar.progress = volume/10
+        tv_volume.text = "" + volume/10
+        VoiceManagerUtil.setCallVoice(activity, volume)
+        SerialPortHelper.setHandsMIC(true)
+        linphoneManager?.enableMic(true)
+
+        when (callState) {
+            0 -> {
+                //去电
+                outGoing = true
+                startUdpCall()
+                Constant.CALL_STATE = Constant.CALL_OUTGOING
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_back2, true, 60000)
+            }
+
+            1 -> {
+                //来电
+                outGoing = false
+                showIncomingCall()
+
+                if (SettingConfig.getAutoAnswer(activity)) {
+                    //自动接听
+                    handler.postDelayed({
+                        if (!callEnded && Constant.CALL_STATE != Constant.CALL_CALLING) {
+                            RingPlayHelper.stopRingTone()
+                            acceptUdpCall()
+                            acceptCall()
+                        }
+                    }, 1500)
+                } else {
+                    RingPlayHelper.playRingTone(baseActivity, R.raw.ring_tone, true, 0)
+                }
+            }
+        }
+    }
+
+    private fun showUI(){
+        view_title_layout_tv_hospital_name.setText(R.string.str_back)
+        view_title_layout_img.visibility = View.VISIBLE
+        view_title_lay_day_img.setImageResource(R.mipmap.riqi_b)
+        view_title_lay_time_img.setImageResource(R.mipmap.shijian_b)
+
+        //网络图标
+        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_fail)
+        }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_w)
+        }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.lanya_b)
+        }
+
+        //白天/黑夜
+        if (Constant.day_state == 0){
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_daylight_w)
+        }else{
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_night_w)
+        }
+        view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_fail)
+
+        call_menu_support.visibility = View.GONE
+        call_menu_call_nurse.visibility = View.GONE
+    }
+
+    override fun bindEvent() {
+        //通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                //结束sip通话
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                sky_voice_call_timer.stop()
+                callEnd(true)
+            } else {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                cancelUdpCall()
+                cancelCall()
+            }
+        }
+
+        //来电拒绝
+        sky_voice_call_ring_reject.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            rejectUdpCall()
+            callEnd(false)
+        }
+
+        //来电接听
+        sky_voice_call_ring_pickup_audio.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_INCOMING
+            acceptUdpCall()
+            acceptCall()
+        }
+
+        sky_voice_call_mute.setOnClickListener {
+            val micEnable = linphoneManager?.micEnabled()
+            Log.d(TAG,"mic enable: $micEnable")
+
+            if (micEnable == true) {
+                linphoneManager?.enableMic(false)
+                sky_voice_call_mute.isSelected = true
+            } else {
+                linphoneManager?.enableMic(true)
+                sky_voice_call_mute.isSelected = false
+            }
+        }
+
+        call_volume_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
+            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+                tv_volume.text = "" + progress
+                if (seekBar!!.progress <= 1) {
+                    tv_volume.text = "1"
+                } else {
+                    tv_volume.text = "" + progress
+                }
+            }
+
+            override fun onStartTrackingTouch(seekBar: SeekBar?) {
+                //
+            }
+
+            override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                if (seekBar!!.progress <= 2) {
+                    VoiceManagerUtil.setCallVoice(activity, 20)
+                } else {
+                    VoiceManagerUtil.setCallVoice(activity, seekBar.progress*10)
+                }
+            }
+        })
+    }
+
+    private fun callTerminate() {
+        linphoneManager?.terminateCall()
+    }
+
+    override fun destroy() {
+        cancelTimer()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        RingPlayHelper.stopRingTone()
+        SerialPortHelper.setCallStatus("0")
+        handler.removeCallbacksAndMessages(null)
+    }
+
+    private fun cancelUdpCall() {
+        //取消分机本机呼叫
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.BED_CALL_CANCEL,
+            Constant.BED_NAME,
+            "",
+            Constant.FJ_NAME,
+            Constant.DEVICE_REGISTER_ID,
+            "",
+            "",
+            "",
+            Constant.SIP_ID,
+            localIp,
+            "",
+            Constant.DEVICE_ID,
+            -1,
+            Constant.PART_ID)
+    }
+
+    private fun acceptUdpCall() {
+        //接听主机呼叫
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.HOST_CALL_ACCEPT,
+            Constant.BED_NAME,
+            "",
+            Constant.FJ_NAME,
+            Constant.DEVICE_REGISTER_ID,
+            "",
+            "",
+            "",
+            Constant.SIP_ID,
+            localIp,
+            "",
+            Constant.DEVICE_ID,
+            -1,
+            Constant.PART_ID)
+    }
+
+    private fun rejectUdpCall() {
+        //拒接主机呼叫
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.HOST_CALL_REJECT,
+            Constant.BED_NAME,
+            "",
+            Constant.FJ_NAME,
+            Constant.DEVICE_REGISTER_ID,
+            "",
+            "",
+            "",
+            Constant.SIP_ID,
+            localIp,
+            "",
+            Constant.DEVICE_ID,
+            -1,
+            Constant.PART_ID)
+    }
+
+    private fun startUdpCall() {
+        //发起分机呼叫,发送udp
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.BED_CALL_OUT,
+            Constant.BED_NAME,
+            "",
+            Constant.FJ_NAME,
+            Constant.DEVICE_REGISTER_ID,
+            "",
+            "",
+            "",
+            Constant.SIP_ID,
+            localIp,
+            "",
+            Constant.DEVICE_ID,
+            -1,
+            Constant.PART_ID)
+
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_incoming.visibility = View.GONE
+        sky_voice_call_timeout.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+        startTimer()
+
+        showOutgoingCall()
+    }
+
+    //去电界面
+    private fun showOutgoingCall() {
+        sky_voice_call_calling_text.setText(R.string.offline_outgoing)
+        SerialPortHelper.setCallStatus("1")
+    }
+
+    //来电界面
+    private fun showIncomingCall() {
+        Constant.CALL_STATE = Constant.CALL_INCOMING
+        sky_voice_call_calling_text.setText(R.string.call_incoming)
+        sky_voice_call_outgoing.visibility = View.GONE
+        if (SettingConfig.getAutoAnswer(activity)) {
+            sky_voice_call_incoming.visibility = View.GONE
+        } else {
+            sky_voice_call_incoming.visibility = View.VISIBLE
+        }
+
+        sky_voice_call_timeout.visibility = View.GONE
+        sky_voice_call_timer.visibility = View.GONE
+        SerialPortHelper.setCallStatus("1")
+    }
+
+    //开始接听
+    private fun acceptCall() {
+        sky_voice_call_calling_text.setText(R.string.call_connecting)
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_incoming.visibility = View.GONE
+        sky_voice_call_timeout.visibility = View.GONE
+        sky_voice_call_timer.visibility = View.GONE
+        cancelTimer()
+    }
+
+    //呼叫取消
+    private fun cancelCall() {
+        cancelTimer()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        SerialPortHelper.setCallStatus("0")
+        callEnd(false)
+    }
+
+    private fun showCalling(audioOnly: Boolean) {
+        if (callEnded) {
+            return
+        }
+
+        showMessage("Call connected!")
+
+        if (audioOnly) {
+            ll_voice_call.visibility = View.VISIBLE
+        } else {
+            //显示视频画面
+            fullscreen_video_frame.visibility = View.VISIBLE
+            pip_video_frame.visibility = View.VISIBLE
+            ll_voice_call.visibility = View.GONE
+        }
+
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        sky_voice_call_calling_text.setText(R.string.call_in_call)
+        sky_voice_call_timeout.visibility = View.GONE
+        cancelTimer()
+        sky_voice_call_timer.visibility = View.VISIBLE
+        sky_voice_call_timer.base = SystemClock.elapsedRealtime()
+        sky_voice_call_timer.start()
+        sky_voice_call_mute.visibility = View.VISIBLE
+        ll_voice_volume_bar.visibility = View.VISIBLE
+
+        //通话时关闭指示灯,防止热量太高导致设备重启
+        SerialPortHelper.setCallStatus("0")
+    }
+
+    //通话结束
+    override fun callEnd(handoff: Boolean) {
+        Log.e(TAG, ">>>>>>>>>>> call end !!!!!!!!!!!!!!!!!!")
+        RingPlayHelper.stopRingTone()
+        countDownTimer.cancel()
+
+        synchronized(this) {
+            if (callEnded) {
+                return
+            }
+            callEnded = true
+
+            if (sky_voice_call_timer != null) {
+                sky_voice_call_timer.stop()
+            }
+
+            callTerminate()
+
+            //sip通话结束,无需再发消息
+            //if (handoff) {
+            //}
+
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            Constant.interactionId = -1
+
+            backToMain()
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.type) {
+            //离线状态udp呼叫
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    //需要判断是否是发给本机设备的
+                    if (udpItem.toMacAddr != Constant.DEVICE_REGISTER_ID) {
+                        return
+                    }
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+
+                    if (UdpIndex.BED_CALL_REJECT == udpItem.index) {
+                        //主机拒接分机呼叫
+                        cancelCall()
+                    } else if (UdpIndex.BED_CALL_ACCEPT == udpItem.index) {
+                        //主机接听分机呼叫
+                        RingPlayHelper.stopRingTone()
+                        acceptCall()
+
+                        if (TextUtils.isEmpty(udpItem.targetSipId) || TextUtils.isEmpty(udpItem.targetSipIp)) {
+                            //通话失败,重置并返回主界面
+                            showMessage("target sipId empty!")
+                            Constant.CALL_STATE = Constant.CALL_STANDBY
+                            if (sky_voice_call_timer != null) {
+                                sky_voice_call_timer.stop()
+                            }
+                            callEnd(true)
+                        } else {
+                            val address = udpItem.targetSipId + "@" + udpItem.targetSipIp
+                            linphoneManager?.startCall(address, false)
+                        }
+                    } else if (UdpIndex.HOST_CALL_CANCEL == udpItem.index) {
+                        //主机取消呼叫分机
+                        RingPlayHelper.stopRingTone()
+                        cancelCall()
+                    }
+                }
+            }
+
+            Constant.EVENT_END_CALL -> {
+                Log.d(TAG, ">>>>>>>>>>>>>> EVENT_END_CALL")
+                showMessage("Call end!")
+                if (messageEvent.getMessage() is String) {
+                    val str = messageEvent.message as String
+                    if (str.equals("cancel")) {
+                        cancelUdpCall()
+                        cancelCall()
+                    } else {
+                        callEnd(true)
+                    }
+                }
+            }
+
+            Constant.EVENT_HOOK_OFF -> {
+                RingPlayHelper.stopRingTone()
+                acceptUdpCall()
+                acceptCall()
+            }
+
+            Constant.SIP_CONNECTED -> {
+                linphoneManager?.enableMic(true)
+                showCalling(true)
+            }
+
+            //外部呼叫按键
+            Constant.EVENT_SERIAL_EVENT -> {
+                if (messageEvent.message is String) {
+                    val serialAction = messageEvent.message as String
+                    if (serialAction == "cancel") {
+                        RingPlayHelper.stopRingTone()
+                        cancelUdpCall()
+                        cancelCall()
+                    } else if (serialAction == "accept") {
+                        RingPlayHelper.stopRingTone()
+                        acceptUdpCall()
+                        acceptCall()
+                    } else if (serialAction == "handoff") {
+                        callEnd(true)
+                    } else if (serialAction == "reject") {
+                        RingPlayHelper.stopRingTone()
+                        rejectUdpCall()
+                        callEnd(false)
+                    }
+                }
+            }
+        }
+    }
+
+}

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

@@ -0,0 +1,153 @@
+package com.wdkl.app.ncs.callingbed.fragment
+
+import android.util.Log
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed.R
+import com.wdkl.app.ncs.callingbed.databinding.MainViewOfflineLayoutBinding
+import com.wdkl.app.ncs.callingbed.helper.Utils
+import com.wdkl.app.ncs.callingbed.launch.CallingbedLaunch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.*
+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.dao.DaoManager
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed.BedMainFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.BedMainFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.dos.EventDO
+import com.wdkl.ncs.android.middleware.model.vo.CustomerInfoVO
+import com.wdkl.ncs.android.middleware.utils.CommonUtils
+import kotlinx.android.synthetic.main.main_view_offline_layout.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class  OfflineMainFragment: BaseFragment<BedMainFragmentPresenter, MainViewOfflineLayoutBinding>(), BedMainFragmentContract.View {
+    val TAG = "OfflineMainFragment"
+
+    private var clickTime: Long = 0
+    private var clickTimes: Int = 1
+
+    private val QR_CODE_URL2 = "http://n.szwdkl.cn?type=BIND_PART_AND_ADDR"
+
+    override fun getLayId(): Int {
+        return R.layout.main_view_offline_layout
+    }
+
+    override fun bindDagger() {
+        CallingbedLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        updateInfo()
+    }
+
+    override fun bindEvent() {
+        ll_bed.setOnClickListener {
+            val time = System.currentTimeMillis()
+            if (time - clickTime < 1500) {
+                clickTimes++
+            } else {
+                clickTimes = 1
+            }
+
+            if (clickTimes >15) {
+                showMessage("enable status bar")
+                Utils.hideStatusBar(activity, false)
+                clickTimes = 1
+            }
+            clickTime = time
+        }
+    }
+
+    override fun destory() {
+    }
+
+    override fun showCustomInfo(customInfo: CustomerInfoVO) {
+        /*if (Constant.convenientServiceEnabled) {
+            //便民服务二维码
+            Thread {
+                val builder = StringBuilder()
+                builder.append(QR_CODE_URL2)
+                builder.append("&no=")
+                builder.append(Constant.PART_UNION_ID)
+                builder.append("&page=shopping")
+                builder.append("&addr=")
+                builder.append(Constant.PART_NAME)
+                builder.append(Constant.BED_NAME)
+                val code = EcodeHelper().createQRImage(builder.toString(), 180, null)
+                activity.runOnUiThread {
+                    ll_qr_code?.visibility = View.VISIBLE
+                    image_qr_code?.setImageBitmap(code)
+                }
+            }.start()
+        } else {
+            ll_qr_code?.visibility = View.GONE
+        }*/
+    }
+
+    override fun showEvents(data: ArrayList<EventDO>) {
+
+    }
+
+    override fun onError(message: String, type: Int) {
+        showMessage(message)
+    }
+
+    override fun onNoNet() {
+        showMessage("none network")
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        }, onMobile = {
+
+        }, offline = {
+
+        })
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    private fun updateInfo() {
+        //获取入住信息
+        val data = DaoManager.getInstance().daoSession.frameBedBeanDao.loadAll()
+        if (data != null && data.size > 0) {
+            Constant.FJ_NAME = data[0].customerName
+            tv_bed_name.text = data[0].customerName
+            bed_medic_name.text = data[0].doctorName
+            bed_medic_name2.text = data[0].nurseName
+            tv_bed_name_title.text = CommonUtils.subStringAfter2(data[0].frameName, "-")
+        } else {
+            tv_bed_name.setText(R.string.str_empty)
+            tv_bed_name_title.text = CommonUtils.subStringAfter2(Constant.BED_NAME, "-")
+        }
+
+        tv_bed_age.setText("")
+        tv_bed_sex.setText("")
+        tv_bed_code.setText("")
+        tv_bed_time.setText("")
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+
+        }
+    }
+}

+ 28 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/helper/SOSHelper.java

@@ -8,6 +8,8 @@ import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.tcp.TcpClient;
 import com.wdkl.ncs.android.middleware.tcp.channel.OtherUtil;
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex;
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil;
 
 
 /*
@@ -37,6 +39,31 @@ public class SOSHelper {
         handler.removeCallbacksAndMessages(null);
         handler.sendEmptyMessageDelayed(110, 120000);
     }
+
+    public static void sosUdpStart() {
+        SerialPortHelper.setSosLight("2");
+
+        UdpSendUtil.getInstance().sendUdpData(
+                UdpIndex.SOS_CALL,
+                Constant.BED_NAME,
+                "",
+                "",
+                Constant.DEVICE_REGISTER_ID,
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                Constant.DEVICE_ID,
+                -1,
+                Constant.PART_ID);
+
+        //60s之后紧急按钮灯自动复位
+        handler.removeCallbacksAndMessages(null);
+        handler.sendEmptyMessageDelayed(110, 120000);
+    }
+
     public static void sostime(Object data) {
         if (Constant.EMERGENCY_ID != -1) {
             TcpModel tcpModel = OtherUtil.SosTime(Constant.EMERGENCY_ID,data);
@@ -47,6 +74,7 @@ public class SOSHelper {
         }
 
     }
+
     public static void sosStop() {
         handler.removeCallbacksAndMessages(null);
         if (Constant.day_state == 1) {

+ 22 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/settings/SettingConfig.java

@@ -115,6 +115,11 @@ public class SettingConfig {
     //主题背景id
     private static final String KEY_SP_DORM_THEME_BG_ID = "KEY_SP_DORM_THEME_BG_ID";
 
+    //是否开启离线模式,由服务器后台配置,默认不开
+    private static final String KEY_SP_OFFLINE_MODE = "KEY_SP_OFFLINE_MODE";
+    //当前是否处于离线模式
+    private static final String KEY_SP_OFFLINE_MODE_RUNNING = "KEY_SP_OFFLINE_MODE_RUNNING";
+
     public static void setSwCall(Context context, boolean on) {
         getEditor(context).putBoolean(KEY_SP_SW_CALL, on).apply();
     }
@@ -261,6 +266,23 @@ public class SettingConfig {
         getEditor(context).putString(KEY_SP_PLAY_CHANNEL, channel).apply();
     }
 
+    public static boolean getOfflineMode(Context context) {
+        return getSP(context).getBoolean(KEY_SP_OFFLINE_MODE, false);
+    }
+
+    public static void setOfflineMode(Context context, boolean enable) {
+        getEditor(context).putBoolean(KEY_SP_OFFLINE_MODE, enable).apply();
+    }
+
+    public static boolean getOfflineModeRunning(Context context) {
+        //return getSP(context).getBoolean(KEY_SP_OFFLINE_MODE_RUNNING, true);
+        return getSP(context).getBoolean(KEY_SP_OFFLINE_MODE_RUNNING, false);
+    }
+
+    public static void setOfflineModeRunning(Context context, boolean enable) {
+        getEditor(context).putBoolean(KEY_SP_OFFLINE_MODE_RUNNING, enable).apply();
+    }
+
     /**
      * 获取分机白天亮度
      *

+ 29 - 0
android_bed/src/main/java/com/wdkl/app/ncs/callingbed/sip/core/LinphoneManager.kt

@@ -231,6 +231,35 @@ class LinphoneManager private constructor(private val context: Context) {
         accountCreator.createProxyConfig()
     }
 
+    fun setLocalProxyConfig(
+        username: String,
+        password: String,
+        domain: String,
+        type: TransportType? = TransportType.Udp
+    ) {
+        core.clearProxyConfig()
+
+        //无服务器时端口必须,固定5060,否则拨打IP时,也需加上端口号。如:sip:192.168.1.100:6666
+        val transports = Factory.instance().createTransports()
+        transports.setUdpPort(5060)
+        transports.setTcpPort(5060)
+        core.setTransports(transports)
+
+        //设置显示名称
+        val accountCreator = core.createAccountCreator(null)
+        accountCreator.language = Locale.getDefault().language
+        accountCreator.reset()
+
+        accountCreator.username = username
+        accountCreator.password = password
+        accountCreator.domain = domain
+        accountCreator.displayName = username
+        accountCreator.transport = type
+
+        val cfg = accountCreator.createProxyConfig()
+        core.setDefaultProxyConfig(cfg)
+    }
+
 
     /**
      * 取消注册

+ 96 - 0
android_bed/src/main/res/layout-land/callingbed_offline_main_new.xml

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+
+        <RelativeLayout
+            android:id="@+id/main_rl_1"
+            android:layout_width="120dp"
+            android:layout_height="match_parent"
+            android:background="@mipmap/bg"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/menu_home"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/d15"
+                android:layout_marginTop="18dp"
+                android:layout_marginRight="@dimen/d15"
+                android:background="@drawable/shape_main_bt_bg"
+                android:drawablePadding="6dp"
+                android:ellipsize="end"
+                android:focusableInTouchMode="true"
+                android:gravity="center"
+                android:padding="10dp"
+                android:singleLine="true"
+                android:text="@string/str_home"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/menu_more"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/menu_home"
+                android:layout_marginLeft="@dimen/d15"
+                android:layout_marginTop="10dp"
+                android:layout_marginRight="@dimen/d15"
+                android:background="@drawable/shape_main_bt_bg"
+                android:drawablePadding="6dp"
+                android:ellipsize="end"
+                android:gravity="center"
+                android:padding="10dp"
+                android:singleLine="true"
+                android:text="@string/str_more"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/menu_call_nurse"
+                android:layout_width="96dp"
+                android:layout_height="96dp"
+                android:layout_alignParentBottom="true"
+                android:layout_marginLeft="10dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginRight="10dp"
+                android:layout_marginBottom="@dimen/d20"
+                android:background="@drawable/selector_callhu_bt"
+                android:drawableTop="@mipmap/hujiao"
+                android:gravity="center"
+                android:paddingLeft="@dimen/d6"
+                android:paddingTop="@dimen/d20"
+                android:paddingRight="@dimen/d6"
+                android:paddingBottom="@dimen/d20"
+                android:singleLine="true"
+                android:text="@string/str_call_nurse"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/main_rl_2"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_toRightOf="@+id/main_rl_1"
+            android:background="#EAF2F9">
+
+            <include
+                android:id="@+id/activity_calling_bed_layout_title_new"
+                layout="@layout/view_title_layout" />
+
+            <!--床位fragment区域-->
+            <FrameLayout
+                android:id="@+id/callingbed_main_frame"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_below="@id/activity_calling_bed_layout_title_new"
+                android:layout_marginLeft="20dp"
+                android:layout_marginBottom="10dp" />
+
+        </RelativeLayout>
+
+    </RelativeLayout>
+</layout>

+ 97 - 0
android_bed/src/main/res/layout/callingbed_offline_main_new.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+
+        <RelativeLayout
+            android:id="@+id/main_rl_1"
+            android:layout_width="120dp"
+            android:layout_height="match_parent"
+            android:background="@mipmap/bg"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/menu_home"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/d15"
+                android:layout_marginTop="18dp"
+                android:layout_marginRight="@dimen/d15"
+                android:background="@drawable/shape_main_bt_bg"
+                android:drawablePadding="6dp"
+                android:ellipsize="end"
+                android:focusableInTouchMode="true"
+                android:gravity="center"
+                android:padding="10dp"
+                android:singleLine="true"
+                android:text="@string/str_home"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/menu_more"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/menu_home"
+                android:layout_marginLeft="@dimen/d15"
+                android:layout_marginTop="10dp"
+                android:layout_marginRight="@dimen/d15"
+                android:background="@drawable/shape_main_bt_bg"
+                android:drawablePadding="6dp"
+                android:ellipsize="end"
+                android:gravity="center"
+                android:padding="10dp"
+                android:singleLine="true"
+                android:text="@string/str_more"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/menu_call_nurse"
+                android:layout_width="96dp"
+                android:layout_height="96dp"
+                android:layout_alignParentBottom="true"
+                android:layout_marginLeft="10dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginRight="10dp"
+                android:layout_marginBottom="@dimen/d20"
+                android:background="@drawable/selector_callhu_bt"
+                android:drawableTop="@mipmap/hujiao"
+                android:gravity="center"
+                android:paddingTop="@dimen/d20"
+                android:paddingBottom="@dimen/d20"
+                android:paddingLeft="@dimen/d6"
+                android:paddingRight="@dimen/d6"
+                android:singleLine="true"
+                android:text="@string/str_call_nurse"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/main_rl_2"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_toRightOf="@+id/main_rl_1"
+            android:background="#EAF2F9">
+
+            <include
+                android:id="@+id/activity_calling_bed_layout_title_new"
+                layout="@layout/view_title_layout" />
+
+            <!--床位fragment区域-->
+            <FrameLayout
+                android:id="@+id/callingbed_main_frame"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_below="@id/activity_calling_bed_layout_title_new"
+                android:layout_marginBottom="10dp"
+                android:layout_marginLeft="20dp" />
+
+        </RelativeLayout>
+
+    </RelativeLayout>
+</layout>

+ 387 - 0
android_bed/src/main/res/layout/main_view_offline_layout.xml

@@ -0,0 +1,387 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:paddingRight="@dimen/d56">
+
+        <RelativeLayout
+            android:id="@+id/main_view_bg"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:background="@mipmap/bingren_bg">
+
+            <LinearLayout
+                android:id="@+id/ll_bed"
+                android:layout_width="@dimen/d260"
+                android:layout_height="@dimen/d250"
+                android:layout_marginLeft="@dimen/d10"
+                android:layout_marginTop="@dimen/d24"
+                android:background="@mipmap/chuanghao_bg2"
+                android:gravity="center"
+                android:orientation="vertical"
+                android:paddingLeft="14dp"
+                android:paddingRight="14dp">
+
+                <com.wdkl.ncs.android.lib.widget.MarqueeTextView
+                    android:id="@+id/tv_bed_name_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="marquee"
+                    android:focusable="true"
+                    android:focusableInTouchMode="true"
+                    android:marqueeRepeatLimit="-1"
+                    android:singleLine="true"
+                    android:text="--"
+                    android:textColor="@color/txt_number2"
+                    android:textSize="100sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/tv_bed_card_no"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="@color/txt_number2"
+                    android:textSize="18sp"
+                    android:visibility="gone" />
+            </LinearLayout>
+
+            <com.wdkl.ncs.android.lib.widget.MarqueeTextView
+                android:id="@+id/tv_bed_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/d20"
+                android:layout_marginTop="@dimen/d24"
+                android:layout_toLeftOf="@id/ll_qr_code"
+                android:layout_toRightOf="@+id/ll_bed"
+                android:ellipsize="marquee"
+                android:focusable="true"
+                android:focusableInTouchMode="true"
+                android:marqueeRepeatLimit="-1"
+                android:singleLine="true"
+                android:text="--"
+                android:textColor="@color/white"
+                android:textSize="100sp" />
+
+            <TextView
+                android:id="@+id/tv_bed_code"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_bed_name"
+                android:layout_alignLeft="@+id/tv_bed_name"
+                android:layout_marginTop="15dp"
+                android:drawableLeft="@mipmap/zhuyuan"
+                android:drawablePadding="@dimen/d10"
+                android:gravity="center"
+                android:text="@string/card_no"
+                android:textColor="@color/white"
+                android:textSize="@dimen/font_size_20" />
+
+            <TextView
+                android:id="@+id/tv_bed_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_bed_code"
+                android:layout_alignLeft="@+id/tv_bed_name"
+                android:layout_marginTop="@dimen/d18"
+                android:drawableLeft="@mipmap/ruyuan"
+                android:drawablePadding="@dimen/d10"
+                android:gravity="center"
+                android:text="@string/indate"
+                android:textColor="@color/white"
+                android:textSize="@dimen/font_size_20" />
+
+            <TextView
+                android:id="@+id/tv_bed_age"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_bed_name"
+                android:layout_alignTop="@+id/tv_bed_code"
+                android:layout_marginLeft="@dimen/d30"
+                android:layout_toRightOf="@+id/tv_bed_code"
+                android:padding="@dimen/d3"
+                android:text="@string/str_age"
+                android:textColor="@color/white"
+                android:textSize="@dimen/font_size_20" />
+
+            <TextView
+                android:id="@+id/tv_bed_sex"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/tv_bed_age"
+                android:layout_alignTop="@+id/tv_bed_time"
+                android:layout_marginLeft="@dimen/d20"
+                android:layout_toRightOf="@+id/tv_bed_time"
+                android:padding="@dimen/d3"
+                android:text="@string/str_gender_none"
+                android:textColor="@color/white"
+                android:textSize="@dimen/font_size_20" />
+
+            <LinearLayout
+                android:id="@+id/ll_qr_code"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_marginTop="@dimen/d24"
+                android:layout_marginRight="@dimen/d6"
+                android:gravity="center"
+                android:visibility="gone">
+
+                <ImageView
+                    android:id="@+id/image_qr_code"
+                    android:layout_width="140dp"
+                    android:layout_height="140dp" />
+
+                <TextView
+                    android:layout_width="20dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="6dp"
+                    android:gravity="center"
+                    android:text="@string/str_service"
+                    android:textSize="20sp" />
+            </LinearLayout>
+
+        </RelativeLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:orientation="horizontal">
+
+            <RelativeLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content">
+
+                <RelativeLayout
+                    android:id="@+id/rv_main_view_s"
+                    android:layout_width="567dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/d48">
+
+                    <TextView
+                        android:id="@+id/bed_f_txt1"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/dengji"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_14" />
+
+                    <TextView
+                        android:id="@+id/bed_f_txt2"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:layout_marginLeft="@dimen/d18"
+                        android:layout_toRightOf="@+id/bed_f_txt1"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/celiang"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_14" />
+
+                    <TextView
+                        android:id="@+id/bed_f_txt3"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:layout_marginLeft="@dimen/d18"
+                        android:layout_toRightOf="@+id/bed_f_txt2"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/geli"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_14" />
+
+                    <TextView
+                        android:id="@+id/bed_f_txt4"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:layout_below="@+id/bed_f_txt1"
+                        android:layout_marginTop="@dimen/d20"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/yinshi"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_14" />
+
+                    <TextView
+                        android:id="@+id/bed_f_txt5"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:layout_below="@+id/bed_f_txt1"
+                        android:layout_marginLeft="@dimen/d18"
+                        android:layout_marginTop="@dimen/d20"
+                        android:layout_toRightOf="@+id/bed_f_txt4"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/guom"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/black"
+                        android:textSize="@dimen/font_size_14" />
+
+                    <TextView
+                        android:id="@+id/bed_f_txt6"
+                        android:layout_width="@dimen/d177"
+                        android:layout_height="@dimen/d89"
+                        android:layout_below="@+id/bed_f_txt1"
+                        android:layout_marginLeft="@dimen/d18"
+                        android:layout_marginTop="@dimen/d20"
+                        android:layout_toRightOf="@+id/bed_f_txt5"
+                        android:background="@drawable/shape_bed_bg"
+                        android:drawableLeft="@mipmap/wu"
+                        android:drawablePadding="@dimen/d14"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="@dimen/d19"
+                        android:paddingRight="@dimen/d29"
+                        android:text="@string/str_empty"
+                        android:textColor="@color/txt_setting"
+                        android:textSize="@dimen/font_size_14" />
+
+                </RelativeLayout>
+            </RelativeLayout>
+
+            <LinearLayout
+                android:layout_width="@dimen/d240"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="@dimen/d18"
+                android:layout_marginTop="@dimen/d18"
+                android:background="@drawable/shape_bed_bg"
+                android:orientation="vertical">
+
+
+                <RelativeLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_marginRight="@dimen/d5"
+                    android:layout_weight="1">
+
+                    <ImageView
+                        android:id="@+id/bed_medic_img"
+                        android:layout_width="@dimen/d43"
+                        android:layout_height="@dimen/d43"
+                        android:layout_centerVertical="true"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:background="@mipmap/ys_img" />
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_centerVertical="true"
+                        android:layout_marginLeft="@dimen/d20"
+                        android:layout_toRightOf="@+id/bed_medic_img"
+                        android:gravity="center"
+                        android:orientation="vertical">
+
+                        <TextView
+                            android:id="@+id/bed_medic_name"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/str_empty"
+                            android:textColor="@color/black"
+                            android:textSize="@dimen/font_size_18" />
+
+                        <TextView
+                            android:id="@+id/bed_medic_info"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_below="@+id/bed_medic_name"
+                            android:layout_marginTop="@dimen/d8"
+                            android:layout_toRightOf="@+id/bed_medic_img"
+                            android:background="@drawable/shape_bed_medic_txt_bg"
+                            android:paddingLeft="@dimen/d13"
+                            android:paddingTop="@dimen/d3"
+                            android:paddingRight="@dimen/d13"
+                            android:paddingBottom="@dimen/d3"
+                            android:text="@string/doctor_title"
+                            android:textColor="@color/white"
+                            android:textSize="@dimen/font_size_16" />
+                    </LinearLayout>
+
+                </RelativeLayout>
+
+                <View
+                    android:id="@+id/f_tx_view"
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="@color/view_bg" />
+
+                <RelativeLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp"
+                    android:layout_marginRight="@dimen/d5"
+
+                    android:layout_weight="1"
+                    android:gravity="center_vertical">
+
+                    <ImageView
+                        android:id="@+id/bed_medic_img2"
+                        android:layout_width="@dimen/d43"
+                        android:layout_height="@dimen/d43"
+                        android:layout_centerVertical="true"
+                        android:layout_marginLeft="@dimen/d40"
+                        android:background="@mipmap/hs_img" />
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_centerVertical="true"
+                        android:layout_marginLeft="@dimen/d20"
+                        android:layout_toRightOf="@+id/bed_medic_img2"
+                        android:gravity="center"
+                        android:orientation="vertical">
+
+                        <TextView
+                            android:id="@+id/bed_medic_name2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/str_empty"
+                            android:textColor="@color/black"
+                            android:textSize="@dimen/font_size_18" />
+
+                        <TextView
+                            android:id="@+id/bed_medic_info2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_below="@+id/bed_medic_name2"
+                            android:layout_marginTop="@dimen/d8"
+                            android:layout_toRightOf="@+id/bed_medic_img2"
+                            android:background="@drawable/shape_bed_nurse_txt_bg"
+                            android:paddingLeft="@dimen/d13"
+                            android:paddingTop="@dimen/d3"
+                            android:paddingRight="@dimen/d13"
+                            android:paddingBottom="@dimen/d3"
+                            android:text="@string/nurse_title"
+                            android:textColor="@color/white"
+                            android:textSize="@dimen/font_size_16" />
+                    </LinearLayout>
+
+                </RelativeLayout>
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+</layout>
+
+

+ 1 - 1
android_bed/src/main/res/layout/view_title_layout.xml

@@ -165,7 +165,7 @@
             android:layout_gravity="center_vertical"
             android:layout_marginLeft="4dp"
             android:layout_marginRight="4dp"
-            android:src="@mipmap/ic_tcp_fail" />
+            android:src="@mipmap/ic_tcp_nor" />
 
         <ImageView
             android:id="@+id/view_title_layout_iv_ethernet"

+ 10 - 0
android_bed/src/main/sharedUserId/AndroidManifest.xml

@@ -195,6 +195,16 @@
             android:screenOrientation="nosensor"
             android:launchMode="singleTask"/>
 
+        <activity android:name="com.wdkl.app.ncs.callingbed.activity.OfflineCallingbedActivity"
+            android:turnScreenOn="true"
+            android:screenOrientation="nosensor"
+            android:launchMode="singleTask" >
+        </activity>
+
+        <activity android:name="com.wdkl.app.ncs.callingbed.activity.CallingbedDormitoryActivity"
+            android:turnScreenOn="true"
+            android:screenOrientation="nosensor"
+            android:launchMode="singleTask"/>
 
         <service android:name="com.wdkl.app.ncs.callingbed.bt_gateway.BluetoothService"/>
         <service android:name="com.wdkl.app.ncs.callingbed.sleep.SleepService"/>

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

@@ -97,6 +97,11 @@
             android:launchMode="singleInstance">
         </activity>
 
+        <activity android:name=".activity.OfflineHomeActivity"
+            android:screenOrientation="landscape"
+            android:launchMode="singleInstance">
+        </activity>
+
         <activity android:name=".activity.CallingHostActivationActivity"
             android:screenOrientation="landscape"
             android:launchMode="singleInstance">

+ 66 - 3
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/CallingHostActivationActivity.kt

@@ -36,6 +36,8 @@ import com.wdkl.ncs.android.lib.vo.filter
 import com.wdkl.ncs.android.middleware.api.UrlManager
 import com.wdkl.ncs.android.middleware.common.Constant
 import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.DeviceInfoBean
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.DeviceContract
 import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.DevicePresenter
 import com.wdkl.ncs.android.middleware.model.ThirdServerInfo
@@ -72,6 +74,9 @@ class CallingHostActivationActivity : BaseActivity<DevicePresenter, CallinghostA
     private var cancelRestart = false
     private var clickTime: Long = 0
 
+    private var homeLaunch = false
+    private var deviceInfo: DeviceInfoBean? = null
+
     override fun getLayId(): Int {
         return R.layout.callinghost_activation
     }
@@ -121,7 +126,45 @@ class CallingHostActivationActivity : BaseActivity<DevicePresenter, CallinghostA
                 e.printStackTrace()
             }
         }
-        checkServer()
+
+        if (SettingConfig.getOfflineModeRunning(activity)) {
+            val devices = DaoManager.getInstance().daoSession.deviceInfoBeanDao.loadAll()
+            if (devices != null && devices.size > 0) {
+                deviceInfo = devices[0]
+            }
+            Log.e(TAG, "goto offline mode: " + deviceInfo)
+
+            if (SettingConfig.getMainDevice(activity) && deviceInfo != null) {
+                Constant.hospital_name = deviceInfo!!.hospitalName
+                Constant.part_name = deviceInfo!!.partName
+                Constant.partDisplay = deviceInfo!!.partDisplay
+                Constant.PART_ID = deviceInfo!!.partId
+                Constant.DEVICE_ID = deviceInfo!!.id
+                Constant.SIP_ID = deviceInfo!!.sipId
+                //Constant.DEVICE_TYPE = deviceInfo!!.deviceType
+
+                if (!TextUtils.isEmpty(NetHelper.getInstance().localIP)) {
+                    //进入离线模式
+                    AppTool.Time.delay(5000) {
+                        if (!homeLaunch) {
+                            homeLaunch = true
+
+                            val intent = Intent()
+                            intent.setClass(activity, OfflineHomeActivity::class.java)
+                            activity.startActivity(intent)
+
+                            finish()
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "没有IP地址")
+                }
+            } else {
+                checkServer()
+            }
+        } else {
+            checkServer()
+        }
     }
 
     override fun bindEvent() {
@@ -190,8 +233,7 @@ class CallingHostActivationActivity : BaseActivity<DevicePresenter, CallinghostA
         }
 
         val netInfo = NetHelper.getNetInfo(activity)
-        activation_v.text =
-            BuildConfig.VERSION_NAME + "_" + BuildConfig.VERSION_CODE + "_" + Build.MODEL
+        activation_v.text = BuildConfig.VERSION_NAME + "_" + BuildConfig.VERSION_CODE + "_" + Build.MODEL
         activation_ip.text = NetHelper.getInstance().localIP
         if (netInfo != null) {
             activation_wg.text = netInfo.gateway
@@ -423,6 +465,10 @@ class CallingHostActivationActivity : BaseActivity<DevicePresenter, CallinghostA
             Constant.back_id = data.backupId
         }
 
+        if (data.hostDeviceMain != null) {
+            SettingConfig.setMainDevice(activity, data.hostDeviceMain)
+        }
+
         if (TextUtils.isEmpty(data.partId.toString()) || TextUtils.isEmpty(data.id.toString())
             || TextUtils.isEmpty(data.sipId)
         ) {
@@ -463,6 +509,23 @@ class CallingHostActivationActivity : BaseActivity<DevicePresenter, CallinghostA
         }
         handler.removeCallbacksAndMessages(null)
 
+        //保存本机设备信息到数据库
+        DaoManager.getInstance().asyncSession.runInTx {
+            Log.d(TAG, "save device info to db")
+            DaoManager.getInstance().daoSession.deviceInfoBeanDao.deleteAll()
+            val deviceInfo = DeviceInfoBean()
+            deviceInfo.hospitalId = data.hospitalId
+            deviceInfo.hospitalName = data.hospitalName
+            deviceInfo.id = data.id
+            deviceInfo.partId = data.partId
+            deviceInfo.partName = data.partName
+            deviceInfo.partDisplay = data.partDisplay
+            deviceInfo.deviceType = data.deviceType
+            deviceInfo.name = data.name
+            deviceInfo.sipId = data.sipId
+            deviceInfo.sipPassword = data.sipPassword
+            DaoManager.getInstance().daoSession.deviceInfoBeanDao.insert(deviceInfo)
+        }
 
         val intent = Intent()
         intent.setClass(activity, NurseHomeActivity::class.java)

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

@@ -22,15 +22,14 @@ import com.wdkl.ncs.android.component.nursehome.util.SpeechUtil
 import com.wdkl.ncs.android.lib.base.BaseActivity
 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.helper.RecordHelper
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.MessageContract
 import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.MessagePresenter
 import com.wdkl.ncs.android.middleware.model.dos.ClerkDO
-import com.wdkl.ncs.android.middleware.model.dos.FrameDO
 import com.wdkl.ncs.android.middleware.model.dos.RemarkDO
 import com.wdkl.ncs.android.middleware.model.vo.RemarksVO
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
-import com.wdkl.ncs.android.middleware.utils.MessageEvent
 import com.wdkl.ncs.android.middleware.utils.ScreenUtils
 import kotlinx.android.synthetic.main.message_create_dialog_activity.*
 import okhttp3.MediaType

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

@@ -7,8 +7,10 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.graphics.Color
 import android.net.ConnectivityManager
 import android.net.Uri
+import android.net.wifi.WifiManager
 import android.os.BatteryManager
 import android.os.Build
 import android.os.Bundle
@@ -46,6 +48,7 @@ import com.wdkl.ncs.android.component.nursehome.visit.frgment.VisitMainFragment
 import com.wdkl.ncs.android.component.nursehome.visit.frgment.VisitSetFragment
 import com.wdkl.ncs.android.component.nursehome.visit.tcp.VisitTcpClient
 import com.wdkl.ncs.android.component.nursehome.window.IncidentWindow
+import com.wdkl.ncs.android.component.nursehome.window.IncidentWindow2
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.lib.core.locale.LocaleMangerUtils
@@ -54,6 +57,8 @@ import com.wdkl.ncs.android.lib.utils.*
 import com.wdkl.ncs.android.middleware.common.Constant
 import com.wdkl.ncs.android.middleware.common.MessageEvent
 import com.wdkl.ncs.android.middleware.common.SipStatus
+import com.wdkl.ncs.android.middleware.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.LedDeviceInfoBean
 import com.wdkl.ncs.android.middleware.entity.BringItem
 import com.wdkl.ncs.android.middleware.entity.CallingItem
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.NurseHomeActivityContract
@@ -72,6 +77,10 @@ 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
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.middleware.udp2.UdpHelper
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import com.wdkl.ncs.android.middleware.utils.StringUtil
 import com.wdkl.ncs.janus.util.JanusConstant
@@ -122,6 +131,10 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
     private var visIntent: Intent? = null
 
+    //udp
+    private var helper: UdpHelper? = null
+    private var wifiManager: WifiManager? = null
+    private var udpRunning = false
 
     companion object {
         var sosItemList = ArrayList<CallingItem>()
@@ -132,7 +145,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
         // 添加字符串到 brIngList 中
         fun addTobrIngList(id: String,bid: String) {
-            var item =  BringItem(bid,id)
+            val item =  BringItem(bid,id)
             brIngList.add(item)
         }
 
@@ -187,6 +200,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
     //事件提醒window
     private var incidentWindow: IncidentWindow? = null
+    private var incidentWindow2: IncidentWindow2? = null
 
     private val localThread = Thread()
 
@@ -281,10 +295,9 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             view_title_layout_iv_sip.visibility = View.GONE
         }
 
-        if(Constant.part_name !=null){
-            view_title_layout_tv_hospital_name.text = Constant.part_name
-        }
+        Constant.offlineMap.clear()
 
+        view_title_layout_tv_hospital_name.text = Constant.partDisplay
         device_id.setText("ID: " + Constant.DEVICE_ID)
 
         currentFragment = FramePartFragment()
@@ -304,20 +317,12 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             }
         }
 
-        //presenter.loadTcpData()
         presenter.loadServerInfo()
         regReceiver()//注册时间广播
 
-//        name_of_organization_tv.text = Constant.partDisplay
-//        Log.e(TAG, Constant.partDisplay)
-//        tv_device_id.text = "ID: " + Constant.DEVICE_ID
-//        home_radio_bt.isChecked = true
-
         updateNetState()
         updateTime(true)
 
-
-
         SpeechUtil.getInstance().init(activity) {
             //如果tts启用失败或者不支持中文则将语音播报切换到音乐模式
             if (!it) {
@@ -328,15 +333,6 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             }
         }
         HardWareFactroy.getHardTools().setSerial(this)
-//        if (!Constant.DEVICE_7INCH) {
-//            setSerialListener()
-//
-//        } else {
-//        //7寸主机
-//            //打开手柄mic
-//            //SerialPortUtilHost.getInstance().setMIC(true)
-//            //SerialPortUtilHost.getInstance().setCallStatus("0")
-//        }
 
         //初始化事件提醒window
         incidentWindow = IncidentWindow(this.activity)
@@ -352,7 +348,9 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         }
 
         Thread {
-            AppUtil.changePingMode()
+            if ("rk3128".equals(Build.MODEL)) {
+                AppUtil.changePingMode()
+            }
 
             try {
                 Thread.sleep(15000)
@@ -390,6 +388,18 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         }
 
     }
+
+    private fun initUdp() {
+        Log.d(TAG, "init udp...$udpRunning")
+        if (!udpRunning) {
+            wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
+            helper = UdpHelper(wifiManager, BaseApplication.appContext)
+            helper?.run()
+
+            udpRunning = true
+        }
+    }
+
     private fun getPower(): Int {
         val mBatteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
         val power = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
@@ -716,6 +726,8 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             HardWareFactroy.getHardTools().toggleStatusBar(activity, true)
         }
         HardWareFactroy.getHardTools().unInit()
+
+        DaoManager.getInstance().closeConnection()
     }
 
     override fun onNoneNet() {
@@ -966,7 +978,6 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
             checkApp()
 
         } catch (e: Exception) {
-            //showMessage("Setting configs error: " + e.message)
             Log.i(TAG, "获取设置配置信息异常" + e.message)
         }
 
@@ -981,6 +992,11 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 showMessage("SIP Empty")
             }
         }
+
+        //todo test: 是否开启离线模式功能
+        //SettingConfig.setOfflineMode(activity, true)
+        //SettingConfig.setOfflineDeviceSize(activity, 1)
+        //initUdp()
     }
 
     fun loadLedDevice() {
@@ -1029,6 +1045,19 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                     Constant.LED_EXIST = true
                 }
                 LedManagerUtils.getInstance().setLedList(ledList)
+
+                //保存LED点阵屏设备
+                DaoManager.getInstance().asyncSession.runInTx {
+                    DaoManager.getInstance().daoSession.ledDeviceInfoBeanDao.deleteAll()
+                    for (led in ledList) {
+                        val ledDevice = LedDeviceInfoBean()
+                        ledDevice.ip = led.ip
+                        ledDevice.fontSize = led.fontSize
+                        ledDevice.resolution = led.resolution
+                        ledDevice.voiceOn = led.voiceOn
+                        DaoManager.getInstance().daoSession.ledDeviceInfoBeanDao.insert(ledDevice)
+                    }
+                }
             }
         } else {
             ledList.clear()
@@ -1140,7 +1169,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
     //显示左侧历史记录界面
     fun showLeftFragment(){
-//        host_call_frame_r.bringToFront()
+        //host_call_frame_r.bringToFront()
         if (currentFragmentThree != null) {
             val transaction = supportFragmentManager.beginTransaction()
             //transaction.setCustomAnimations(R.anim.slide_in_from_right, 0)
@@ -1250,6 +1279,36 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 }*/
             }
         }
+
+        //是否切换离线模式
+        if (SettingConfig.getOfflineMode(activity)) {
+            Log.e(TAG, "offline device: " + Constant.offlineMap.size)
+            if (Constant.offlineMap.size >= SettingConfig.getOfflineDeviceSize(activity)) {
+                SettingConfig.setOfflineModeRunning(activity, true)
+
+                AppTool.Time.delay(5000) {
+                    AppUpdateHelper.restartApp(activity)
+                }
+            }
+
+            //通知设备切换在线模式
+            UdpSendUtil.getInstance().sendUdpData(
+                UdpIndex.SERVER_MODE_ONLINE,
+                Constant.part_name,
+                "",
+                "",
+                Constant.DEVICE_REGISTER_ID,
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                Constant.DEVICE_ID,
+                -1,
+                Constant.PART_ID
+            )
+        }
     }
 
 
@@ -1334,8 +1393,8 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                         val callTcp = VoiceUtil.voiceTransfer(item.tid, Constant.DEVICE_ID, item.interactionVO.fromDeviceId, item.interactionVO.id)
                         TcpClient.getInstance().sendMsg(callTcp.toJson())
 
-                        EventBus.getDefault().post(MessageEvent(item.interactionVO, Constant.EVENT_TRANSFER_CALL))
-                        iterator2.remove()
+                        EventBus.getDefault().post(MessageEvent(item, Constant.EVENT_TRANSFER_CALL))
+                        //iterator2.remove()
                     }
                     break
                 }
@@ -1624,9 +1683,15 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         if (Constant.TCP_CONNECTED) {
             view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_success)
             //view_title_layout_tv_point.setBackgroundResource(R.mipmap.sip_b)
+
+            view_title_layout_tv_hospital_name.text = Constant.partDisplay
+            view_title_layout_tv_hospital_name.setTextColor(Color.WHITE)
         } else {
-            view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_fail)
+            view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_nor)
             //view_title_layout_tv_point.setBackgroundResource(R.mipmap.sip_h)
+
+            //view_title_layout_tv_hospital_name.setText(R.string.disconnect_server)
+            view_title_layout_tv_hospital_name.setTextColor(Color.parseColor("#FFFD3B30"))
         }
     }
     private fun updateLeftBtState(isbg :Boolean) {
@@ -1987,7 +2052,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
 
                         //是否开启呼叫转移
                         //if (SettingConfiguration.getInstance().transferCall) {
-                        val sosItem = CallingItem(tcpModel.tid, System.currentTimeMillis(), interactionVO, tcpModel.action)
+                        val sosItem = CallingItem(System.currentTimeMillis(), tcpModel.tid, interactionVO, tcpModel.action, 0, null)
                         sosItemList.add(sosItem)
                         //}
 
@@ -2633,6 +2698,39 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
                 addCallFragment(fragment)
             }
 
+            //离线状态udp消息
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+                    if (UdpIndex.SOS_CALL == udpItem.index) {
+                        //紧急呼叫优先级最高,如果当前有通话或正在呼叫的需要将其打断
+                        RingPlayHelper.stopRingTone()
+                        SpeechUtil.getInstance().stopSpeak(false)
+                        if (Constant.CALL_STATE == Constant.CALL_OUTGOING) {
+                            EventBus.getDefault().post(MessageEvent("cancel", Constant.EVENT_END_CALL))
+                        } else if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                            EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_END_CALL))
+                        }
+
+                        showSosAlert2(udpItem)
+
+                        //LedHelper.updateLedInfo(interactionVO, true, true)
+
+                        val sosName = Util.appendSpace(udpItem.fromFrameName)
+                        if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
+                            val text = BaseApplication.appContext.getString(R.string.sos_call_speech, sosName)
+                            SpeechUtil.getInstance().addSpeech(text, true)
+                        } else {
+                            RingPlayHelper.playRingTone(activity, R.raw.sos2, true)
+                        }
+                    } else if (UdpIndex.DEVICE_OFFLINE == udpItem.index) {
+                        //分机离线消息
+                        Constant.offlineMap.put(udpItem.fromMacAddr, System.currentTimeMillis())
+                    }
+                }
+            }
+
         }
 
     }
@@ -2661,6 +2759,14 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNewN
         }
     }
 
+    private fun showSosAlert2(item: UdpItem) {
+        if (incidentWindow2 == null) {
+            incidentWindow2 = IncidentWindow2(activity)
+        }
+
+        incidentWindow2?.createFloatView(item)
+    }
+
     private fun declineSosItem(item: InteractionVO){
         if(incidentWindow!!.view==null){
             return

Разница между файлами не показана из-за своего большого размера
+ 1095 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/OfflineHomeActivity.kt


+ 1 - 1
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/SystemActivity.kt

@@ -185,7 +185,7 @@ class SystemActivity : BaseActivity<SystemActivityPresenter, CallingbedSettingMa
             //设备网关
             settings_main_8_tx.text = netInfo.gateway
             //设备dns
-            settings_main_9_tx.text = netInfo.dns1+ netInfo.netMask
+            settings_main_9_tx.text = netInfo.dns1 + "/" + netInfo.netMask
         }
         //服务器地址
         val buildUrl = UrlManager.build()

+ 125 - 39
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/adapter/CallingItemAdapter.kt

@@ -1,6 +1,7 @@
 package com.wdkl.ncs.android.component.nursehome.adapter
 
 import android.content.Context
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -9,6 +10,7 @@ import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
 import com.wdkl.ncs.android.component.nursehome.R
 import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
+import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.nursehome.util.RingPlayHelper
 import com.wdkl.ncs.android.component.nursehome.util.SpeechUtil
 import com.wdkl.ncs.android.component.nursehome.util.TimeTransition
@@ -17,6 +19,9 @@ import com.wdkl.ncs.android.middleware.common.MessageEvent
 import com.wdkl.ncs.android.middleware.entity.CallingItem
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
 import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
 import org.greenrobot.eventbus.EventBus
 
 class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHolder> {
@@ -30,7 +35,7 @@ class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHol
     }
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParentViewHolder {
-        val view = LayoutInflater.from(parent?.context).inflate(R.layout.adapter_calling_item, parent, false)
+        val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_calling_item, parent, false)
         val viewHolder = ParentViewHolder(view)
 
         return viewHolder
@@ -39,37 +44,54 @@ class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHol
     override fun onBindViewHolder(holder: ParentViewHolder, position: Int) {
         try {
             val callingItem = callingData.get(position)
-            val itemData = callingItem.interactionVO
-            var frameName: String? = ""
-            var memberName: String? = ""
-            if (DeviceTypeEnum.DOCTOR_HOST.value() == itemData.fromDeviceType
-                || DeviceTypeEnum.NURSE_HOST.value() == itemData.fromDeviceType
-                || DeviceTypeEnum.OTHER_HOST.value() == itemData.fromDeviceType) {
-                //医生机,护士主机,其他主机,总控主机等
-                frameName = itemData.fromDeviceName
-            } else if (DeviceTypeEnum.NURSE_WATCH.value() == itemData.fromDeviceType) {
-                //移动设备
-                frameName = itemData.fromMemberName
+            if (callingItem.type == 1) {
+                //udp
+                val itemData = callingItem.udpItem
+                holder.callingBedName.text = itemData.fromFrameName
+                holder.callingName.text = itemData.patientName
+                holder.callingTime.text = TimeTransition.stampToDateTime(callingItem.startTime)
+
+                if (SettingConfig.getOfflineModeRunning(context)) {
+                    holder.callingAccept.visibility = View.VISIBLE
+                    holder.callingAccept.setOnClickListener {
+                        EventBus.getDefault().post(MessageEvent(itemData, Constant.EVENT_ACCEPT_CALL))
+                        removeCall(itemData, true)
+                    }
+                } else {
+                    holder.callingAccept.visibility = View.GONE
+                }
             } else {
-                //其他
-                frameName = itemData.fromFrameFullName
-                memberName = itemData.fromMemberName
-            }
+                //tcp
+                val itemData = callingItem.interactionVO
+                var frameName: String? = ""
+                var memberName: String? = ""
+                if (DeviceTypeEnum.DOCTOR_HOST.value() == itemData.fromDeviceType
+                    || DeviceTypeEnum.NURSE_HOST.value() == itemData.fromDeviceType
+                    || DeviceTypeEnum.OTHER_HOST.value() == itemData.fromDeviceType
+                ) {
+                    //医生机,护士主机,其他主机,总控主机等
+                    frameName = itemData.fromDeviceName
+                } else if (DeviceTypeEnum.NURSE_WATCH.value() == itemData.fromDeviceType) {
+                    //移动设备
+                    frameName = itemData.fromMemberName
+                } else {
+                    //其他
+                    frameName = itemData.fromFrameFullName
+                    memberName = itemData.fromMemberName
+                }
 
-            holder?.callingBedName?.text = frameName
-            holder?.callingName?.text = memberName
-            if (itemData.createDate != null) {
-                holder?.callingTime?.text = TimeTransition.stampToDateTime(itemData.createDate * 1000)
-            }
+                holder.callingBedName.text = frameName
+                holder.callingName.text = memberName
+                if (itemData.createDate != null) {
+                    holder.callingTime.text = TimeTransition.stampToDateTime(itemData.createDate * 1000)
+                }
 
-            holder?.callingAccept?.setOnClickListener {
-                EventBus.getDefault().post(MessageEvent(callingItem, Constant.EVENT_ACCEPT_CALL))
-                removeCall(itemData, true)
+                holder.callingAccept.visibility = View.VISIBLE
+                holder.callingAccept.setOnClickListener {
+                    EventBus.getDefault().post(MessageEvent(callingItem, Constant.EVENT_ACCEPT_CALL))
+                    removeCall(itemData, true)
+                }
             }
-
-            /*holder?.callingReject?.setOnClickListener {
-                removeCall(itemData)
-            }*/
         } catch (ex : Exception) {
             ex.printStackTrace()
         }
@@ -81,15 +103,25 @@ class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHol
 
     fun addCall(callingItem: CallingItem) {
         synchronized(this) {
-            val iterator = NurseHomeActivity.callingList.iterator()
-            while (iterator.hasNext()) {
-                val it = iterator.next()
-                if (callingItem.interactionVO.fromDeviceId == it.interactionVO.fromDeviceId) {
-                    iterator.remove()
+            try {
+                val iterator = NurseHomeActivity.callingList.iterator()
+                while (iterator.hasNext()) {
+                    val it = iterator.next()
+                    if (callingItem.type == 1) {
+                        if (callingItem.udpItem.fromMacAddr.equals(it.udpItem.fromMacAddr)) {
+                            iterator.remove()
+                        }
+                    } else {
+                        if (callingItem.interactionVO.id.equals(it.interactionVO.id)) {
+                            iterator.remove()
+                        }
+                    }
                 }
+                NurseHomeActivity.callingList.add(callingItem)
+                updateCallList()
+            } catch (e: Exception) {
+                e.printStackTrace()
             }
-            NurseHomeActivity.callingList.add(callingItem)
-            updateCallList()
         }
     }
 
@@ -122,19 +154,17 @@ class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHol
         }
     }
 
-    fun removeCallByPos(position: Int, stop: Boolean) {
+    fun removeCall(udpItem: UdpItem, stop: Boolean) {
         if (stop) {
             RingPlayHelper.stopRingTone()
             SpeechUtil.getInstance().stopSpeak(true)
         }
 
         synchronized(this) {
-            val removeItem = callingData.get(position)
-            EventBus.getDefault().post(MessageEvent(removeItem, Constant.EVENT_REJECT_CALL))
             val iterator = NurseHomeActivity.callingList.iterator()
             while (iterator.hasNext()) {
                 val it = iterator.next()
-                if (removeItem.interactionVO.id.equals(it.interactionVO.id)) {
+                if (udpItem.fromMacAddr.equals(it.udpItem.fromMacAddr)) {
                     iterator.remove()
                 }
             }
@@ -143,10 +173,66 @@ class CallingItemAdapter : RecyclerView.Adapter<CallingItemAdapter.ParentViewHol
         }
     }
 
+    fun removeCallByPos(position: Int, stop: Boolean) {
+        if (stop) {
+            RingPlayHelper.stopRingTone()
+            SpeechUtil.getInstance().stopSpeak(true)
+        }
+
+        synchronized(this) {
+            try {
+                val removeItem = callingData.get(position)
+                if (removeItem.type == 1) {
+                    rejectCall(removeItem.udpItem)
+                } else {
+                    EventBus.getDefault().post(MessageEvent(removeItem, Constant.EVENT_REJECT_CALL))
+                }
+
+                val iterator = NurseHomeActivity.callingList.iterator()
+                while (iterator.hasNext()) {
+                    val it = iterator.next()
+                    if (removeItem.type == 1) {
+                        if (removeItem.udpItem.fromMacAddr.equals(it.udpItem.fromMacAddr)) {
+                            iterator.remove()
+                        }
+                    } else {
+                        if (removeItem.interactionVO.id.equals(it.interactionVO.id)) {
+                            iterator.remove()
+                        }
+                    }
+                }
+
+                updateCallList()
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+    }
+
     fun setUpdateCallback(callBack: UpdateCallback) {
         updateCallback = callBack
     }
 
+    private fun rejectCall(item: UdpItem) {
+        //发送udp拒接呼叫
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.BED_CALL_REJECT,
+            item.fromFrameName,
+            "",
+            item.patientName,
+            Constant.DEVICE_REGISTER_ID,
+            item.fromMacAddr,
+            "",
+            "",
+            "",
+            "",
+            "",
+            Constant.DEVICE_ID,
+            item.fromDeviceId,
+            Constant.PART_ID)
+
+        //LedHelper.updateLedInfo(item, false, false)
+    }
 
 
     class ParentViewHolder : RecyclerView.ViewHolder {

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

@@ -0,0 +1,121 @@
+package com.wdkl.ncs.android.component.nursehome.adapter
+
+import android.content.Context
+import android.util.Log
+import android.view.ViewGroup
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.GridLayoutHelper
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.OfflineHomeActivity
+import com.wdkl.ncs.android.component.nursehome.databinding.AdapterOfflineHomeFrameBedLayBinding
+import com.wdkl.ncs.android.component.nursehome.util.RingPlayHelper
+import com.wdkl.ncs.android.component.nursehome.util.SpeechUtil
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.utils.then
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.dao.entity.FrameBedBean
+import org.greenrobot.eventbus.EventBus
+
+
+class OfflineFrameBedAdapter(var context: Context, val data: ArrayList<FrameBedBean>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterOfflineHomeFrameBedLayBinding>, FrameBedBean>() {
+
+    var TAG = OfflineFrameBedAdapter::class.java.getSimpleName()
+
+    private var clickTime: Long = 0
+
+    /**
+     * 数据提供者
+     */
+    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 {
+        Log.i("OfflineFrameBedAdapter", "" + data.size)
+
+        return GridLayoutHelper(6).then { self ->
+            /**取消自动填充*/
+            self.setAutoExpand(false)
+
+            /**设置左右间距*/
+            self.hGap = 10
+
+            /**设置上下间距*/
+            self.vGap = 10
+
+            /**设置Margin*/
+            self.setMargin(0, 20, 0, 0)
+        }
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterOfflineHomeFrameBedLayBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_offline_home_frame_bed_lay)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterOfflineHomeFrameBedLayBinding>, position: Int) {
+        holder.bind { binding ->
+            try {
+                val itemData = getItem(position)
+                Log.e("FrameBedVosAdapter ", "show bed...." + itemData.frameName + ", " + itemData.customerName)
+                binding.bedLayFNumber.text = itemData.frameName
+                if (itemData.customerName != null) {
+                    binding.bedLayName.text = itemData.customerName
+                } else {
+                    binding.bedLayName.setText(R.string.empty_bed)
+                }
+
+                binding.ivCallBed.setOnClickListener {
+                    //呼叫床位
+                    if (System.currentTimeMillis() - clickTime < 3000) {
+                        showMessage(R.string.call_wait)
+                    } else {
+                        if (itemData.bedDeviceId != null) {
+                            //呼出时停止语音播报及铃声
+                            SpeechUtil.getInstance().stopSpeak(true)
+                            RingPlayHelper.stopRingTone()
+                            if (OfflineHomeActivity.checkIncomingCall(itemData.bedDeviceMac)) {
+                                showMessage(R.string.call_in_list)
+                            } else {
+                                EventBus.getDefault().post(MessageEvent(itemData, Constant.EVENT_CALL_OUT))
+                            }
+                        } else {
+                            showMessage(R.string.no_custom)
+                        }
+                    }
+                    clickTime = System.currentTimeMillis()
+                }
+            } catch (e: Exception) {
+                //
+            }
+
+        }
+    }
+
+}

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

@@ -21,6 +21,7 @@ interface NurseHomeComponent{
     fun inject(activity: SickbedFragment)
     fun inject(activity: NurseMoveFragment)
     fun inject(activity: OtherHostFragment)
+    fun inject(activity: OfflineHomeActivity)
 
     fun inject(activity: TrustManagementFragment)
     fun inject(activity: SystemSettingsFragment)
@@ -53,9 +54,10 @@ interface NurseHomeComponent{
     fun inject(fragment: VisitMainFragment)
     fun inject(fragment: VisitCallFragment)
     fun inject(fragment: VisitSetFragment)
-
     fun inject(fragment: MessageFragment)
     fun inject(fragment: IotDeviceFragment)
+    fun inject(fragment: OfflineBedFragment)
+    fun inject(fragment: OfflineCallRecordsFragment)
 
     fun inject(fragment: PushMessageFragment)
     fun inject(fragment: PushMessageOrdinaryFragment)

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

@@ -31,8 +31,6 @@ abstract class BaseCallFragment: Fragment(), View.OnTouchListener {
     protected var tid: String? = ""
     protected var fromId: Int = -1
 
-    //protected var gEngineKit: SkyEngineKit? = null
-
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)

+ 93 - 12
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/CallRecordsFragment.kt

@@ -38,6 +38,8 @@ 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
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
 import kotlinx.android.synthetic.main.fragment_call_records.*
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
@@ -407,6 +409,71 @@ class CallRecordsFragment: BaseFragment<CallRecordsFragmentPresenter, FragmentCa
                 updateRecord()
             }
 
+            //离线状态udp消息
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+                    if (UdpIndex.BED_CALL_OUT == udpItem.index) {
+                        val mainActivity = activity as? NurseHomeActivity
+                        EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_FINISHh))
+                        mainActivity?.showLeftFragment()
+
+                        //1.更新点阵屏信息显示
+                        //LedHelper.updateLedInfo(interactionVO, true, false)
+                        //2.语音播报并显示呼叫
+                        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_ACCEPT
+                        ) {
+                            if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
+                                val frameName: String
+                                if (Locale.CHINESE.getLanguage().equals(language)) {
+                                    frameName = Util.appendSpace(udpItem.fromFrameName.replace("-", ","))
+                                } else {
+                                    frameName = udpItem.fromFrameName.replace("-", "")
+                                }
+                                val text = BaseApplication.appContext.getString(R.string.voice_call_speech, frameName)
+                                SpeechUtil.getInstance().addSpeech(text, false)
+                            } else if (SettingConfig.getTtsMode(activity) == SettingConfig.RING_ON) {
+                                RingPlayHelper.playRingTone(activity, R.raw.ring_tone, true)
+                            } else if (SettingConfig.getTtsMode(activity) == SettingConfig.MUSIC_ON) {
+                                RingPlayHelper.playRingTone(activity, R.raw.incoming_call, true)
+                            }
+                        }
+
+                        val item = CallingItem(System.currentTimeMillis(), "", null, null, 1, udpItem)
+                        if (callingAdapter != null) {
+                            callingAdapter?.addCall(item)
+                        }
+                    } else if (UdpIndex.BED_CALL_CANCEL.equals(udpItem.index)) {
+                        //对方取消呼叫
+                        //LedHelper.updateLedInfo(interactionVO, false, false)
+                        if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
+                            val frameName: String
+                            if (Locale.CHINESE.getLanguage().equals(language)) {
+                                frameName = Util.appendSpace(udpItem.fromFrameName.replace("-", ","))
+                            } else {
+                                frameName = udpItem.fromFrameName.replace("-", "")
+                            }
+                            val text = BaseApplication.appContext.getString(R.string.voice_call_speech, frameName)
+                            SpeechUtil.getInstance().removeSpeak(text)
+                        } else {
+                            //如果呼叫列表只有一个呼叫了,说明删除这个之后就清空了,此时关闭铃声或音乐
+                            if (NurseHomeActivity.callingList.size == 1) {
+                                RingPlayHelper.stopRingTone()
+                            }
+                        }
+
+                        if (callingAdapter != null) {
+                            callingAdapter?.removeCall(udpItem, false)
+                        }
+                    }
+                }
+            }
+
             Constant.EVENT_TCP_MSG -> {
                 val tcpModel = messageEvent.getMessage() as TcpModel
                 Log.d("tcp", "tcpType: " + tcpModel.type + ", tcpAction: " + tcpModel.action)
@@ -463,7 +530,7 @@ class CallRecordsFragment: BaseFragment<CallRecordsFragmentPresenter, FragmentCa
                         }
                         //3.刷新呼叫列表
                         updateRecord()
-                        val item = CallingItem(tcpModel.tid, System.currentTimeMillis(), interactionVO, tcpModel.action)
+                        val item = CallingItem(System.currentTimeMillis(), tcpModel.tid, interactionVO, tcpModel.action, 0, null)
                         if (callingAdapter != null) {
                             callingAdapter?.addCall(item)
                         }
@@ -515,27 +582,41 @@ class CallRecordsFragment: BaseFragment<CallRecordsFragmentPresenter, FragmentCa
                     val callingItem = NurseHomeActivity.callingList.get(0)
                     val itemData = callingItem.interactionVO
                     //LedHelper.updateLedInfo(itemData, false, false)
-                    EventBus.getDefault().post(MessageEvent(callingItem, Constant.EVENT_ACCEPT_CALL))
-                    if (callingAdapter != null) {
-                        callingAdapter?.removeCall(itemData, true)
+
+                    if (callingItem.type == 0) {
+                        //tcp
+                        EventBus.getDefault().post(MessageEvent(callingItem, Constant.EVENT_ACCEPT_CALL))
+                        if (callingAdapter != null) {
+                            callingAdapter?.removeCall(itemData, true)
+                        }
                     }
                 }
             }
 
             Constant.EVENT_TRANSFER_CALL -> {
-                val transferData = messageEvent.getMessage() as InteractionVO
-                if (callingAdapter != null) {
-                    callingAdapter?.removeCall(transferData, false)
+                val callingItem = messageEvent.getMessage() as CallingItem
+                if (callingItem.type == 0) {
+                    if (callingAdapter != null) {
+                        callingAdapter?.removeCall(callingItem.interactionVO, false)
+                    }
                 }
             }
 
             Constant.EVENT_REMOVE_CALL -> {
                 val callingItem = messageEvent.getMessage() as CallingItem
-                val callTcp = VoiceUtil.voiceReject(callingItem.tid, Constant.DEVICE_ID, callingItem.interactionVO.fromDeviceId, callingItem.interactionVO.id)
-                TcpClient.getInstance().sendMsg(callTcp.toJson())
-                LedHelper.updateLedInfo(callingItem.interactionVO, false, false)
-                if (callingAdapter != null) {
-                    callingAdapter?.removeCall(callingItem.interactionVO, false)
+                if (callingItem.type == 0) {
+                    val callTcp = VoiceUtil.voiceReject(
+                        callingItem.tid,
+                        Constant.DEVICE_ID,
+                        callingItem.interactionVO.fromDeviceId,
+                        callingItem.interactionVO.id
+                    )
+                    TcpClient.getInstance().sendMsg(callTcp.toJson())
+                    LedHelper.updateLedInfo(callingItem.interactionVO, false, false)
+
+                    if (callingAdapter != null) {
+                        callingAdapter?.removeCall(callingItem.interactionVO, false)
+                    }
                 }
             }
         }

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

@@ -2,6 +2,7 @@ package com.wdkl.ncs.android.component.nursehome.fragment
 
 
 import android.content.Intent
+import android.util.Log
 import com.alibaba.android.vlayout.DelegateAdapter
 import com.alibaba.android.vlayout.VirtualLayoutManager
 import com.enation.javashop.net.engine.model.NetState
@@ -19,12 +20,15 @@ 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.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.FrameBedBean
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.FramePartContract
 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 com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import kotlinx.android.synthetic.main.fragment_frame_part.*
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
@@ -190,16 +194,49 @@ class FramePartFragment: BaseFragment<FramePartPresenter, FragmentFramePartBindi
             if (SettingConfig.getIsAgora(activity)){
                 Thread {
                     if (!BuildConfig.DEBUG){
-                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL, Constant.VISIT_TCP_PORT!!.toInt(), Constant.reader_idle_time!!.toInt())
+                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL, Constant.VISIT_TCP_PORT, Constant.reader_idle_time)
                     }else{
-                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL_text, Constant.VISIT_TCP_PORT!!.toInt(), Constant.reader_idle_time!!.toInt())
+                        VisitTcpClient.getInstance().init(Constant.SLEEP_TCP_SERVER_URL_text, Constant.VISIT_TCP_PORT, Constant.reader_idle_time)
                     }
                 }.start()
             }
             val frameRoomVos = data.frameRoomVos as ArrayList<FrameRoomVO>
+
+            //添加房间列表
+            val frameBedVO = ArrayList<FrameBedVO>()
+            for (frameRoomVo in frameRoomVos) {
+                val frameBedVOs = frameRoomVo.frameBedList
+                if (frameBedVOs != null && frameBedVOs.size > 0) {
+                    frameBedVO.addAll(frameBedVOs)
+                }
+            }
+            CommonUtils.setInBedVos(frameBedVO)
+
             adapter!!.data.clear()
             adapter!!.data.addAll(frameRoomVos)
             adapter!!.notifyDataSetChanged()
+
+            if (SettingConfig.getOfflineMode(activity)) {
+                //更新床位数据库
+                DaoManager.getInstance().asyncSession.runInTx {
+                    Log.e(TAG, "update device db start")
+                    DaoManager.getInstance().daoSession.frameBedBeanDao.deleteAll()
+                    for (frame in CommonUtils.getInBedVOS()) {
+                        val frameBean = FrameBedBean()
+                        frameBean.id = frame.frameBed.id
+                        frameBean.partId = frame.frameBed.partId
+                        frameBean.frameName = frame.frameBed.fullName
+                        frameBean.customerName = frame.customerName
+                        frameBean.deviceSipId = frame.deviceSipId
+                        frameBean.bedDeviceId = frame.bedDeviceId
+                        frameBean.bedDeviceMac = frame.bedDeviceMac
+
+                        //Log.e(TAG, "insert device in db: ${frameBean.id}, ${frameBean.fullName}, ${frameBean.bedDeviceId}")
+                        DaoManager.getInstance().daoSession.frameBedBeanDao.insert(frameBean)
+                    }
+                    Log.e(TAG, "update device db end")
+                }
+            }
         }
         refresh.finishRefresh()
         refresh.finishLoadMore()

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

@@ -13,10 +13,10 @@ import com.wdkl.ncs.android.component.nursehome.settingconfig.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.IotDeviceContract
 import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.IotDevicePresenter
 import com.wdkl.ncs.android.middleware.model.vo.DeviceVO
-import com.wdkl.ncs.android.middleware.utils.MessageEvent
 import kotlinx.android.synthetic.main.fragment_message.*
 import kotlinx.android.synthetic.main.fragment_nb_iot_device.*
 import org.greenrobot.eventbus.Subscribe

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

@@ -18,12 +18,12 @@ import com.wdkl.ncs.android.lib.base.BaseApplication
 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.MessageContract
 import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.MessagePresenter
 import com.wdkl.ncs.android.middleware.model.dos.ClerkDO
 import com.wdkl.ncs.android.middleware.model.dos.RemarkDO
 import com.wdkl.ncs.android.middleware.model.vo.RemarksVO
-import com.wdkl.ncs.android.middleware.utils.MessageEvent
 import kotlinx.android.synthetic.main.fragment_message.*
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode

+ 205 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineBedFragment.kt

@@ -0,0 +1,205 @@
+package com.wdkl.ncs.android.component.nursehome.fragment
+
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.scwang.smartrefresh.layout.footer.ClassicsFooter
+import com.wdkl.ncs.android.component.nursehome.R
+
+import com.wdkl.ncs.android.component.nursehome.adapter.OfflineFrameBedAdapter
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentSickbedBinding
+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.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.dao.DaoManager
+import com.wdkl.ncs.android.middleware.dao.entity.FrameBedBean
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.SickbedContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.SickbedPresenter
+import com.wdkl.ncs.android.middleware.model.vo.*
+
+import kotlinx.android.synthetic.main.fragment_sickbed.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+
+class OfflineBedFragment : BaseFragment<SickbedPresenter, FragmentSickbedBinding>(), SickbedContract.View {
+
+    var TAG = OfflineBedFragment::class.java.getSimpleName()
+
+    private var adapter: OfflineFrameBedAdapter? = null
+
+    /**
+     * @Name  virtualLayoutManager
+     * @Type  VirtualLayoutManager
+     * @Note  VLayoutManager
+     */
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+
+    /**
+     * @Name  delegateAdapter
+     * @Type  DelegateAdapter
+     * @Note  七巧板适配器
+     */
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:32
+     * @Note   提供layoutID
+     * @return layoutId
+     */
+    override fun getLayId(): Int {
+        return R.layout.fragment_sickbed
+    }
+
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:33
+     * @Note   初始化依赖注入
+     */
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:33
+     * @Note   初始化操作
+     */
+    override fun init() {
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+
+        adapter = OfflineFrameBedAdapter(activity, ArrayList())
+        delegateAdapter.addAdapter(adapter)
+        mViewDataBinding.sickbedRefresh.setRefreshFooter(ClassicsFooter(activity))
+
+        /**配置到RecycleView*/
+        sickbed_listView.layoutManager = virtualLayoutManager
+        sickbed_listView.adapter = delegateAdapter
+        sickbed_refresh.setEnableRefresh(false)
+        sickbed_refresh.setEnableLoadMore(false)
+
+        //加载数据
+        DaoManager.getInstance().asyncSession.runInTx {
+            val data = DaoManager.getInstance().daoSession.frameBedBeanDao.loadAll()
+            activity.runOnUiThread {
+                adapter!!.data.clear()
+                adapter!!.data.addAll(data as ArrayList<FrameBedBean>)
+                adapter!!.notifyDataSetChanged()
+            }
+        }
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:33
+     * @Note   绑定事件
+     */
+    override fun bindEvent() {
+        //
+    }
+
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:34
+     * @Note   页面销毁回调
+     */
+    override fun destory() {
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:35
+     * @Note   处理错误信息
+     * @param  message  错误信息
+     */
+    override fun onError(message: String, type: Int) {
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:37
+     * @Note   耗时加载开始
+     */
+    override fun start() {
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/8/16 下午4:08
+     * @Note   渲染楼层
+     * @param  data  数据
+     */
+    override fun showData(data: FramePartVO) {
+        //
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:36
+     * @Note   操作完成
+     * @param  message 完成信息
+     */
+    override fun complete(message: String, type: Int) {
+    }
+
+    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)
+        }
+
+    }
+
+    /**
+     * @author LDD
+     * @From   HomeFragment
+     * @Date   2018/1/19 下午5:39
+     * @Note   处理网络状态
+     * @param  state 网络状态
+     */
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        }, onMobile = {
+
+        }, offline = {
+
+        })
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        //
+    }
+
+
+}

+ 386 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineCallFragment.kt

@@ -0,0 +1,386 @@
+package com.wdkl.ncs.android.component.nursehome.fragment
+
+import android.os.*
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import android.widget.SeekBar
+import com.google.gson.Gson
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.nursehome.sip.core.LinphoneManager
+import com.wdkl.ncs.android.component.nursehome.util.NetHelper
+import com.wdkl.ncs.android.component.nursehome.util.RingPlayHelper
+import com.wdkl.ncs.android.component.nursehome.util.VoiceManagerUtil
+import com.wdkl.ncs.android.lib.base.BaseApplication
+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.model.bean.SettingConfiguration
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
+import kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class OfflineCallFragment: BaseCallFragment() {
+    private val TAG = "SipCallFragment"
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    private var callEnded: Boolean = false
+    private var outGoing: Boolean = false
+
+    var fromDeviceId = -1
+    var toDeviceId = -1
+
+    //呼叫倒计时
+    lateinit var countDownTimer: CountDownTimer
+
+    private var volume = 60
+
+    private var linphoneManager: LinphoneManager? = null
+
+    override fun getLayId(): Int {
+        return R.layout.sky_voice_call_layout
+    }
+
+    override fun init() {
+        initCountDownTimer()
+
+        linphoneManager = LinphoneManager.getInstance(BaseApplication.appContext)
+        linphoneManager?.setMicGainDb(0f)
+
+        volume = SettingConfig.getHostCallVolume(activity)
+        if (volume < 0 || volume > 100) {
+            volume = 60
+        }
+        call_volume_bar.progress = volume/10
+        tv_volume.text = "" + volume/10
+
+        VoiceManagerUtil.setCallVoice(activity, volume)
+        linphoneManager?.enableMic(true)
+
+        Log.d(TAG, "callState: $callState, local sip: ${Constant.SIP_ID}, target sip: ${Constant.targetSipId}")
+        when (callState) {
+            0 -> {
+                //发起通话
+                outGoing = true
+                showCallView(true)
+                Constant.CALL_STATE = Constant.CALL_OUTGOING
+                Constant.IN_CALL = true
+                RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.ring_back2, true)
+
+                startUdpCall()
+            }
+
+            1 -> {
+                //接受通话
+                outGoing = false
+                showCallView(false)
+                Constant.CALL_STATE = Constant.CALL_CALLING
+                acceptUdpCall()
+            }
+        }
+    }
+
+    private fun initCountDownTimer() {
+        if (SettingConfiguration.getInstance().sipOvertime <= 0) {
+            SettingConfiguration.getInstance().sipOvertime = 30
+        }
+
+        countDownTimer = object: CountDownTimer(SettingConfiguration.getInstance().sipOvertime * 1000L, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+                //
+            }
+
+            override fun onFinish() {
+                //呼叫超时,退出呼叫界面
+                showMessage(R.string.no_response)
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                cancelUdpCall()
+                callEnd(false)
+            }
+        }
+    }
+
+    override fun bindEvent() {
+        //通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                callEnd(true)
+            } else {
+                countDownTimer.cancel()
+                cancelUdpCall()
+                callEnd(false)
+            }
+
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+        }
+
+        sky_voice_call_mute.setOnClickListener {
+            val micEnable = linphoneManager?.micEnabled()
+            Log.d(TAG,"mic enable: $micEnable")
+
+            if (micEnable == true) {
+                linphoneManager?.enableMic(false)
+                sky_voice_call_mute.isSelected = true
+            } else {
+                linphoneManager?.enableMic(true)
+                sky_voice_call_mute.isSelected = false
+            }
+        }
+
+        call_volume_bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
+            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+                tv_volume.text = "" + progress
+                if (seekBar!!.progress <= 1) {
+                    tv_volume.text = "1"
+                } else {
+                    tv_volume.text = "" + progress
+                }
+            }
+
+            override fun onStartTrackingTouch(seekBar: SeekBar?) {
+                //
+            }
+
+            override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                if (seekBar!!.progress <= 2) {
+                    VoiceManagerUtil.setCallVoice(activity, 20)
+                } else {
+                    VoiceManagerUtil.setCallVoice(activity, seekBar.progress*10)
+                }
+            }
+        })
+    }
+
+    private fun callTerminate() {
+        linphoneManager?.terminateCall()
+    }
+
+    override fun destroy() {
+        Constant.showCall = false
+        Constant.IN_CALL = false
+        RingPlayHelper.stopRingTone()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        handler.removeCallbacksAndMessages(null)
+    }
+
+    //开始接听
+    private fun showCallView(outgoing: Boolean) {
+        sky_call_name.text = callName
+        if (outgoing) {
+            countDownTimer.start()
+            sky_voice_call_calling_text.setText(R.string.call_in_calling)
+        } else {
+            sky_voice_call_calling_text.setText(R.string.call_connecting)
+        }
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+        Constant.showCall = true
+    }
+
+    private fun showCalling(audioOnly: Boolean) {
+        if (callEnded) {
+            return
+        }
+
+        showMessage("Call connected!")
+
+        if (Constant.hookOn) {
+            //免提
+            toggleSpeaker(true)
+        } else {
+            //听筒
+            toggleSpeaker(false)
+        }
+
+        if (audioOnly) {
+            ll_voice_call.visibility = View.VISIBLE
+        } else {
+            //显示视频画面
+            fullscreen_video_frame.visibility = View.VISIBLE
+            pip_video_frame.visibility = View.VISIBLE
+            ll_voice_call.visibility = View.GONE
+
+            if (visiting) {
+                visit_list_view.setVisibility(View.VISIBLE)
+            }
+        }
+
+        sky_voice_call_calling_text.setText(R.string.call_in_call)
+        sky_voice_call_timer.visibility = View.VISIBLE
+        sky_voice_call_timer.base = SystemClock.elapsedRealtime()
+        sky_voice_call_timer.start()
+        sky_voice_call_mute.visibility = View.VISIBLE
+        ll_voice_volume_bar.visibility = View.VISIBLE
+    }
+
+    private fun startUdpCall() {
+        //主机呼叫分机
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.HOST_CALL_BED,
+            Constant.part_name,
+            "",
+            "",
+            Constant.DEVICE_REGISTER_ID,
+            targetId,
+            "",
+            "",
+            Constant.SIP_ID,
+            NetHelper.getInstance().localIP,
+            "",
+            Constant.DEVICE_ID,
+            toDeviceId,
+            Constant.PART_ID
+        )
+    }
+
+    private fun acceptUdpCall() {
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.BED_CALL_ACCEPT,
+            callName,
+            "",
+            "",
+            Constant.DEVICE_REGISTER_ID,
+            targetId,
+            "",
+            "",
+            Constant.SIP_ID,
+            NetHelper.getInstance().localIP,
+            "",
+            Constant.DEVICE_ID,
+            fromDeviceId,
+            Constant.PART_ID)
+    }
+
+    private fun cancelUdpCall() {
+        //取消主机呼叫
+        UdpSendUtil.getInstance().sendUdpData(
+            UdpIndex.HOST_CALL_CANCEL,
+            Constant.part_name,
+            "",
+            "",
+            Constant.DEVICE_REGISTER_ID,
+            targetId,
+            "",
+            "",
+            Constant.SIP_ID,
+            NetHelper.getInstance().localIP,
+            "",
+            Constant.DEVICE_ID,
+            toDeviceId,
+            Constant.PART_ID)
+    }
+
+    //通话结束
+    private fun callEnd(handoff: Boolean) {
+        Log.e(TAG, ">>>>>>>>>>> call end !!!!!!!!!!!!!!!!!!")
+        RingPlayHelper.stopRingTone()
+        countDownTimer.cancel()
+        synchronized(this) {
+            if (callEnded) {
+                return
+            }
+            callEnded = true
+
+            if (sky_voice_call_timer != null) {
+                sky_voice_call_timer.stop()
+            }
+
+            callTerminate()
+
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            //if (handoff) {
+            //}
+            Constant.interactionId = null
+
+            backToMain()
+        }
+    }
+
+    private fun toggleSpeaker(enable: Boolean) {
+        Log.d(TAG, "toggle speaker: $enable")
+
+        //rk3128主机无需免提切换
+        if (Build.MODEL != "rk3128") {
+            linphoneManager?.enableSpeaker(enable)
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            //离线状态udp呼叫
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+
+                    if (UdpIndex.HOST_CALL_REJECT == udpItem.index) {
+                        //分机拒接主机呼叫
+                        showMessage(R.string.call_reject)
+                        Constant.CALL_STATE = Constant.CALL_STANDBY
+                        callEnd(false)
+                    } else if (UdpIndex.HOST_CALL_ACCEPT == udpItem.index) {
+                        //分机接听主机呼叫
+                        RingPlayHelper.stopRingTone()
+                        sky_voice_call_calling_text.setText(R.string.call_connecting)
+                        Constant.CALL_STATE = Constant.CALL_CALLING
+                        countDownTimer.cancel()
+
+                        if (TextUtils.isEmpty(udpItem.targetSipId) || TextUtils.isEmpty(udpItem.targetSipIp)) {
+                            //通话失败,重置并返回主界面
+                            showMessage("target sipId is null!")
+                            Constant.CALL_STATE = Constant.CALL_STANDBY
+                            if (sky_voice_call_timer != null) {
+                                sky_voice_call_timer.stop()
+                            }
+
+                            countDownTimer.cancel()
+                            cancelUdpCall()
+                            callEnd(false)
+                        } else {
+                            val address = udpItem.targetSipId + "@" + udpItem.targetSipIp
+                            linphoneManager?.startCall(address, false)
+                        }
+                    }
+                }
+            }
+
+            Constant.EVENT_END_CALL -> {
+                Log.d(TAG, ">>>>>>>>>>>>>> EVENT_END_CALL")
+                showMessage("Call end!")
+                if (messageEvent.getMessage() is String) {
+                    val str = messageEvent.getMessage() as String
+                    if (str.equals("cancel")) {
+                        cancelUdpCall()
+                        callEnd(false)
+                    } else {
+                        callEnd(true)
+                    }
+                }
+            }
+
+            Constant.SIP_CONNECTED -> {
+                Constant.IN_CALL = true
+                showCalling(true)
+            }
+
+            Constant.EVENT_TOGGLE_SPEAKER -> {
+                if (Constant.hookOn) {
+                    //手柄放下
+                    toggleSpeaker(true)
+                } else {
+                    toggleSpeaker(false)
+                }
+            }
+        }
+    }
+
+}

+ 302 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/OfflineCallRecordsFragment.kt

@@ -0,0 +1,302 @@
+package com.wdkl.ncs.android.component.nursehome.fragment
+
+import android.util.Log
+import android.view.View
+import android.view.animation.Animation
+import android.view.animation.AnimationSet
+import android.view.animation.TranslateAnimation
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.scwang.smartrefresh.layout.footer.ClassicsFooter
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
+import com.wdkl.ncs.android.component.nursehome.activity.OfflineHomeActivity
+import com.wdkl.ncs.android.component.nursehome.adapter.CallRecordsItemAdapter
+import com.wdkl.ncs.android.component.nursehome.adapter.CallingItemAdapter
+import com.wdkl.ncs.android.component.nursehome.databinding.FragmentCallRecordsBinding
+import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.nursehome.util.*
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.core.locale.LocaleMangerUtils
+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.entity.CallingItem
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.CallRecordsFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.CallRecordsFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.vo.EventVO
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import kotlinx.android.synthetic.main.fragment_call_records.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.*
+import kotlin.collections.ArrayList
+
+/**
+ * 呼叫记录Fragment
+ */
+class OfflineCallRecordsFragment: BaseFragment<CallRecordsFragmentPresenter, FragmentCallRecordsBinding>(), CallRecordsFragmentContract.View,
+    View.OnClickListener, CallingItemAdapter.UpdateCallback {
+
+    var TAG = OfflineCallRecordsFragment::class.java.getSimpleName()
+
+    private var callingAdapter: CallingItemAdapter? = null
+
+    private var language = "zh"
+
+    private val animationSet = AnimationSet(true)
+
+    override fun getLayId(): Int {
+        return R.layout.fragment_call_records
+    }
+
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        callingAdapter = CallingItemAdapter(activity, ArrayList())
+        rv_calling_list.layoutManager = VirtualLayoutManager(activity)
+        rv_calling_list.adapter = callingAdapter
+
+        val touchCallback: ItemTouchHelper.Callback =
+            object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
+                override fun onMove(
+                        recyclerView: RecyclerView,
+                        viewHolder: RecyclerView.ViewHolder,
+                        target: RecyclerView.ViewHolder
+                ): Boolean {
+                    return false
+                }
+
+                override fun onSwiped(
+                    viewHolder: RecyclerView.ViewHolder,
+                    direction: Int
+                ) {
+                    val pos = viewHolder.adapterPosition
+                    if (callingAdapter != null && pos < callingAdapter!!.itemCount) {
+                        callingAdapter!!.removeCallByPos(pos, true)
+                    }
+                }
+            }
+        val itemTouchHelper = ItemTouchHelper(touchCallback)
+        itemTouchHelper.attachToRecyclerView(rv_calling_list)
+
+        callingAdapter?.setUpdateCallback(this)
+
+        language = LocaleMangerUtils.getApplicationLocale().language
+
+        val translateAnimation = TranslateAnimation(
+            //X轴初始位置
+            Animation.RELATIVE_TO_SELF, 1.0f,
+            //X轴移动的结束位置
+            Animation.RELATIVE_TO_SELF, 0.0f,
+            //y轴开始位置
+            Animation.RELATIVE_TO_SELF, 0.0f,
+            //y轴移动后的结束位置
+            Animation.RELATIVE_TO_SELF, 0.0f
+        )
+        translateAnimation.duration = 300
+        animationSet.fillAfter = true
+        animationSet.addAnimation(translateAnimation)
+
+        //离线模式只显示呼叫列表
+        no_answer_calls_linlyout.visibility = View.GONE
+        call_records_linlyout.visibility = View.GONE
+        ll_call_list.visibility = View.VISIBLE
+        ll_call_record_list.visibility = View.GONE
+    }
+
+    override fun onHiddenChanged(hidden: Boolean) {
+        super.onHiddenChanged(hidden)
+        if (!hidden) {
+            rl_call_records.startAnimation(animationSet)
+        }
+    }
+
+    /**
+     * 按钮监听
+     */
+    override fun onClick(p0: View) {
+        when(p0.id){
+            R.id.call_f_l_view -> {
+                val mainActivity = activity as? OfflineHomeActivity
+                mainActivity?.hideFragment()
+            }
+
+            R.id.call_r_k -> {
+                val mainActivity = activity as? OfflineHomeActivity
+                mainActivity?.hideFragment()
+            }
+        }
+    }
+
+    override fun onUpdate() {
+        val num = NurseHomeActivity.callingList.size
+        if (num > 9) {
+            calling_num_tv.visibility = View.VISIBLE
+            calling_num_tv.text = "9+"
+        } else if (num > 0) {
+            calling_num_tv.visibility = View.VISIBLE
+            calling_num_tv.text = "" + num
+        } else {
+            calling_num_tv.visibility = View.GONE
+            if (Constant.newMissedCall) {
+                missed_call_tips_tv.visibility = View.VISIBLE
+            } else {
+                //当前没有呼叫且没有未接,隐藏列表
+                (activity as? OfflineHomeActivity)?.hideFragment()
+            }
+        }
+        (activity as? OfflineHomeActivity)?.updateCallTips()
+    }
+
+    override fun bindEvent() {
+        //点击监听
+        mViewDataBinding.callFLView.setOnClickListener(this)
+        mViewDataBinding.callRK.setOnClickListener(this)
+    }
+
+    override fun destory() {
+    }
+
+    override fun onError(message: String, type: Int) {
+        refresh.finishRefresh()
+        showMessage(message)
+    }
+
+    override fun start() {
+    }
+    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)
+        }
+
+    }
+
+    override fun renderFloor(data: ArrayList<InteractionVO>) {
+    }
+
+    override fun showEventdata(data: EventVO) {
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            //离线状态udp呼叫
+            Constant.EVENT_UDP -> {
+                if (messageEvent.message is UdpItem) {
+                    val udpItem = messageEvent.message as UdpItem
+                    Log.d(TAG, "EVENT_UDP ==> $udpItem")
+                    if (UdpIndex.BED_CALL_OUT == udpItem.index) {
+                        val mainActivity = activity as? OfflineHomeActivity
+                        EventBus.getDefault().post(MessageEvent("finish", Constant.EVENT_FINISHh))
+                        mainActivity?.showLeftFragment()
+
+                        //1.更新点阵屏信息显示
+                        //LedHelper.updateLedInfo(interactionVO, true, false)
+                        //2.语音播报并显示呼叫
+                        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_ACCEPT
+                        ) {
+                            if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
+                                val frameName: String
+                                if (Locale.CHINESE.getLanguage().equals(language)) {
+                                    frameName = Util.appendSpace(udpItem.fromFrameName.replace("-", ","))
+                                } else {
+                                    frameName = udpItem.fromFrameName.replace("-", "")
+                                }
+                                val text = BaseApplication.appContext.getString(R.string.voice_call_speech, frameName)
+                                SpeechUtil.getInstance().addSpeech(text, false)
+                            } else if (SettingConfig.getTtsMode(activity) == SettingConfig.RING_ON) {
+                                RingPlayHelper.playRingTone(activity, R.raw.ring_tone, true)
+                            } else if (SettingConfig.getTtsMode(activity) == SettingConfig.MUSIC_ON) {
+                                RingPlayHelper.playRingTone(activity, R.raw.incoming_call, true)
+                            }
+                        }
+
+                        val item = CallingItem(System.currentTimeMillis(), "", null, null, 1, udpItem)
+                        if (callingAdapter != null) {
+                            callingAdapter?.addCall(item)
+                        }
+                    } else if (UdpIndex.BED_CALL_CANCEL.equals(udpItem.index)) {
+                        //对方取消呼叫
+                        //LedHelper.updateLedInfo(interactionVO, false, false)
+                        if (SettingConfig.getTtsMode(activity) == SettingConfig.TTS_ON) {
+                            val frameName: String
+                            if (Locale.CHINESE.getLanguage().equals(language)) {
+                                frameName = Util.appendSpace(udpItem.fromFrameName.replace("-", ","))
+                            } else {
+                                frameName = udpItem.fromFrameName.replace("-", "")
+                            }
+                            val text = BaseApplication.appContext.getString(R.string.voice_call_speech, frameName)
+                            SpeechUtil.getInstance().removeSpeak(text)
+                        } else {
+                            //如果呼叫列表只有一个呼叫了,说明删除这个之后就清空了,此时关闭铃声或音乐
+                            if (NurseHomeActivity.callingList.size == 1) {
+                                RingPlayHelper.stopRingTone()
+                            }
+                        }
+
+                        if (callingAdapter != null) {
+                            callingAdapter?.removeCall(udpItem, false)
+                        }
+                    }
+                }
+            }
+
+            Constant.EVENT_HOOK_OFF -> {
+                //接听呼叫列表中第一个电话
+                if (NurseHomeActivity.callingList.size > 0) {
+                    val callingItem = NurseHomeActivity.callingList.get(0)
+                    //LedHelper.updateLedInfo(itemData, false, false)
+
+                    if (callingItem.type == 1) {
+                        //udp
+                        EventBus.getDefault().post(MessageEvent(callingItem.udpItem, Constant.EVENT_ACCEPT_CALL))
+                        if (callingAdapter != null) {
+                            callingAdapter?.removeCall(callingItem.udpItem, true)
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+}

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

@@ -101,7 +101,7 @@ class SipCallFragment: BaseCallFragment() {
 
             override fun onFinish() {
                 //呼叫超时,退出呼叫界面
-                showMessage("无响应")
+                showMessage(R.string.no_response)
                 DeviceChannel.calling = false
                 Constant.CALL_STATE = Constant.CALL_STANDBY
                 cancelCall(Constant.DEVICE_ID, Constant.targetDeviceId, Constant.interactionId)
@@ -295,7 +295,7 @@ class SipCallFragment: BaseCallFragment() {
 
                             if (TextUtils.isEmpty(curIt.toSipId)) {
                                 //通话失败,重置并返回主界面
-                                showMessage("Core或targetSipId为空!")
+                                showMessage("targetSipId is null!")
                                 Constant.CALL_STATE = Constant.CALL_STANDBY
                                 if (sky_voice_call_timer != null) {
                                     sky_voice_call_timer.stop()

+ 37 - 23
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/WorkFragment.kt

@@ -16,6 +16,7 @@ import com.wdkl.ncs.android.component.nursehome.activity.WebviewActivity
 import com.wdkl.ncs.android.component.nursehome.adapter.DeviceMenuapter
 import com.wdkl.ncs.android.component.nursehome.databinding.WorkLayBinding
 import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
 import com.wdkl.ncs.android.lib.base.BaseFragment
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.lib.vo.filter
@@ -45,8 +46,7 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
     var type: String? = ""
     var name: String? = ""
     private var allOrders = ArrayList<DeviceMenulist>()
-    private lateinit var deviceMenuapter : DeviceMenuapter
-    private lateinit var timer: CountDownTimer
+    private lateinit var deviceMenuAdapter : DeviceMenuapter
 
     override fun getLayId(): Int {
         return R.layout.work_lay
@@ -60,9 +60,20 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
         recycler.setVisibility(View.VISIBLE)
         val layoutManager = GridLayoutManager(getActivity(), 3)
         recycler.setLayoutManager(layoutManager)
-        var part_id =Constant.DEVICE_REGISTER_ID
-        if (part_id!=null){
-            presenter.DeviceMenulist(part_id)
+
+        deviceMenuAdapter = DeviceMenuapter(activity, allOrders)
+        deviceMenuAdapter.setOnItemClickListener(this)
+        recycler.adapter = deviceMenuAdapter
+
+        //离线模式只显示设备信息菜单
+        if (SettingConfig.getOfflineModeRunning(activity)) {
+            showDefaultDeviceMenu()
+        } else {
+            if (!TextUtils.isEmpty(Constant.DEVICE_REGISTER_ID)) {
+                presenter.DeviceMenulist(Constant.DEVICE_REGISTER_ID)
+            } else {
+                showMessage("none reg id")
+            }
         }
     }
     //点击事件
@@ -91,19 +102,31 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
                     }
                 }
             }
-            deviceMenuapter = DeviceMenuapter(activity, allOrders)
-            deviceMenuapter.setOnItemClickListener(this)
-            recycler.adapter = deviceMenuapter
-            deviceMenuapter.updateData(allOrders)
+
+            deviceMenuAdapter.updateData(allOrders)
         }
 
     }
 
+    private fun showDefaultDeviceMenu() {
+        //添加默认设备菜单
+        val deviceMenu = DeviceMenulist()
+        deviceMenu.name = getString(R.string.str_device_info)
+        deviceMenu.type = "ACT"
+        deviceMenu.act_name = "SystemActivity"
+        deviceMenu.actived = 1
+        allOrders.add(deviceMenu)
+        deviceMenuAdapter.updateData(allOrders)
+    }
+
     override fun onNoneNet() {
+        showMessage("None net")
+        showDefaultDeviceMenu()
     }
 
     override fun onError(message: String, type: Int) {
         showMessage(message)
+        showDefaultDeviceMenu()
     }
 
     override fun complete(message: String, type: Int) {
@@ -117,12 +140,6 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
         super.onStart()
     }
 
-    override fun onResume() {
-        super.onResume()
-    }
-    override fun onPause() {
-        super.onPause()
-    }
     override fun onStop() {
         if (EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().unregister(this);
@@ -140,9 +157,6 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
         })
     }
 
-
-
-
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
         when (messageEvent.getType()) {
@@ -164,10 +178,7 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
                 intent.putExtra("key", text)
                 intent.setClass(activity, WebviewActivity::class.java)
                 activity.startActivity(intent)
-                // 输出结果
-                //            showMessage(text)
             }else if (allOrders.get(keyId).type.equals("ACT")){
-
                 if (allOrders.get(keyId).act_name.equals("SystemActivity")){
                     //设备信息
                     val intent = Intent()
@@ -190,12 +201,15 @@ class WorkFragment : BaseFragment<BedNursingWorkFragmentPresenter, WorkLayBindin
                     //消息推送
                     val mainActivity = activity as? NurseHomeActivity
                     mainActivity?.showMiddleFragment(PushMessageFragment(),false)
+                } else {
+                    showMessage(R.string.str_action_error)
                 }
+            } else {
+                showMessage(R.string.str_type_error)
             }
         } catch (e: Exception) {
-            showMessage("结果异常,请在后台设置")
+            showMessage(R.string.str_result_error)
         }
     }
-    // 从设备信息中获取属性值的方法
 
 }

+ 1 - 3
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/hardware/imp/Z3128HardTools.java

@@ -85,11 +85,9 @@ public class Z3128HardTools extends HardTools {
 
     @Override
     public void setSerial(NurseHomeActivity activity) {
-            //串口监听
+        //串口监听
         activity.setSerialListener();
         SerialPortUtilHost.getInstance().powerIndicator(1);
-
-
     }
 
     @Override

+ 0 - 1
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/hardware/imp/ZKEHardTools.java

@@ -86,7 +86,6 @@ public class ZKEHardTools extends HardTools {
     public void setSerial(NurseHomeActivity activity) {
         activity.kaerregReceiver();
         activity.updatePower();
-
     }
 
     @Override

+ 39 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/settingconfig/SettingConfig.java

@@ -174,6 +174,13 @@ public class SettingConfig {
     //托管时是否进入托管,1进入,0不进入
     private static final String KEY_SP_BoolTransfer = "KEY_SP_BoolTransfer";
 
+    private static final String KEY_SP_MAIN_DEVICE = "KEY_SP_MAIN_DEVICE";
+    //是否开启离线模式,由服务器后台配置,默认不开
+    private static final String KEY_SP_OFFLINE_MODE = "KEY_SP_OFFLINE_MODE";
+    //当前是否处于离线模式
+    private static final String KEY_SP_OFFLINE_MODE_RUNNING = "KEY_SP_OFFLINE_MODE_RUNNING";
+    private static final String KEY_SP_OFFLINE_DEVICE_SIZE = "KEY_SP_OFFLINE_DEVICE_SIZE";
+
     public static void setBoolTransfer(Context context, int mode) {
         getEditor(context).putInt(KEY_SP_BoolTransfer, mode).apply();
     }
@@ -346,6 +353,38 @@ public class SettingConfig {
         getEditor(context).putBoolean(KEY_SP_NB_VOICE_WARNING, enable).apply();
     }
 
+    public static boolean getMainDevice(Context context) {
+        return getSP(context).getBoolean(KEY_SP_MAIN_DEVICE, true);
+    }
+
+    public static void setMainDevice(Context context, boolean b) {
+        getEditor(context).putBoolean(KEY_SP_MAIN_DEVICE, b).apply();
+    }
+
+    public static boolean getOfflineMode(Context context) {
+        return getSP(context).getBoolean(KEY_SP_OFFLINE_MODE, false);
+    }
+
+    public static void setOfflineMode(Context context, boolean enable) {
+        getEditor(context).putBoolean(KEY_SP_OFFLINE_MODE, enable).apply();
+    }
+
+    public static boolean getOfflineModeRunning(Context context) {
+        return getSP(context).getBoolean(KEY_SP_OFFLINE_MODE_RUNNING, false);
+    }
+
+    public static void setOfflineModeRunning(Context context, boolean enable) {
+        getEditor(context).putBoolean(KEY_SP_OFFLINE_MODE_RUNNING, enable).apply();
+    }
+
+    public static int getOfflineDeviceSize(Context context) {
+        return getSP(context).getInt(KEY_SP_OFFLINE_DEVICE_SIZE, 1);
+    }
+
+    public static void setOfflineDeviceSize(Context context, int size) {
+        getEditor(context).putInt(KEY_SP_OFFLINE_DEVICE_SIZE, size).apply();
+    }
+
     /**
      * 获取播报次数
      *

+ 29 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/sip/core/LinphoneManager.kt

@@ -233,6 +233,35 @@ class LinphoneManager private constructor(private val context: Context) {
         accountCreator.createProxyConfig()
     }
 
+    fun setLocalProxyConfig(
+        username: String,
+        password: String,
+        domain: String,
+        type: TransportType? = TransportType.Udp
+    ) {
+        core.clearProxyConfig()
+
+        //无服务器时端口必须,固定5060,否则拨打IP时,也需加上端口号。如:sip:192.168.1.100:6666
+        val transports = Factory.instance().createTransports()
+        transports.setUdpPort(5060)
+        transports.setTcpPort(5060)
+        core.setTransports(transports)
+
+        //设置显示名称
+        val accountCreator = core.createAccountCreator(null)
+        accountCreator.language = Locale.getDefault().language
+        accountCreator.reset()
+
+        accountCreator.username = username
+        accountCreator.password = password
+        accountCreator.domain = domain
+        accountCreator.displayName = username
+        accountCreator.transport = type
+
+        val cfg = accountCreator.createProxyConfig()
+        core.setDefaultProxyConfig(cfg)
+    }
+
 
     /**
      * 取消注册

+ 1 - 9
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/window/IncidentWindow.kt

@@ -292,7 +292,7 @@ class IncidentWindow(var activity: Activity) {
             return BaseRecyclerViewHolder.build(parent, R.layout.adapter_event_list_item)
         }
         override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterEventListItemBinding>, position: Int) {
-            holder?.bind { binding ->
+            holder.bind { binding ->
                 val tcpModel = getItem(position)
                 val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                 //房间名称
@@ -415,14 +415,6 @@ class IncidentWindow(var activity: Activity) {
                 }
             }
         }
-
-        override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterEventListItemBinding>, p1: Int, payloads: MutableList<Any>) {
-
-        }
-
-
-
-
     }
 
 }

+ 222 - 0
android_host/src/main/java/com/wdkl/ncs/android/component/nursehome/window/IncidentWindow2.kt

@@ -0,0 +1,222 @@
+package com.wdkl.ncs.android.component.nursehome.window
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Build
+import android.view.*
+import android.view.animation.AlphaAnimation
+import android.view.animation.Animation
+import android.view.animation.LinearInterpolator
+import androidx.databinding.DataBindingUtil
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.databinding.AdapterEventListItemBinding
+import com.wdkl.ncs.android.component.nursehome.databinding.WindowIncidentBinding
+import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.nursehome.util.*
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.udp2.UdpIndex
+import com.wdkl.ncs.android.middleware.udp2.UdpItem
+import com.wdkl.ncs.android.middleware.udp2.UdpSendUtil
+
+class IncidentWindow2(var contexts: Context) {
+    var mWindowManager: WindowManager? = null
+    var view: View? = null
+    var eventList = ArrayList<UdpItem>()
+
+    var adapter: EventListAdapter? = null
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    //创建悬浮按钮
+    fun createFloatView(udpItem: UdpItem) {
+        //如果列表中已经存在则不再增加
+        for(event in eventList) {
+            if (udpItem.fromMacAddr.equals(event.fromMacAddr)) {
+                return
+            }
+        }
+
+        eventList.add(udpItem)
+        //如果当前没有显示则创建view并显示,如果已经显示则仅更新事件列表
+        if (view == null) {
+            val wmParams = WindowManager.LayoutParams()
+            //获取的是WindowManagerImpl.CompatModeWrapper
+            mWindowManager = contexts.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+                //设置window type 这个只能在8.0以下系统使用
+                wmParams.type = WindowManager.LayoutParams.TYPE_PHONE
+            } else {
+                //8.0及以上系统使用这个
+                wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+            }
+
+            //设置图片格式,效果为背景透明
+            wmParams.format = PixelFormat.RGBA_8888
+            //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
+            //wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+            //调整悬浮窗显示的停靠位置为左侧置顶
+//        wmParams.gravity = Gravity.RIGHT or Gravity.TOP
+            wmParams.gravity = Gravity.CENTER_HORIZONTAL;
+            // 以屏幕左上角为原点,设置x、y初始值,相对于gravity
+            wmParams.x = 0
+            wmParams.y = 0
+            //设置悬浮窗口长宽数据
+            wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT
+            wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT
+
+            //获取浮动窗口视图所在布局
+            view = LayoutInflater.from(contexts).inflate(R.layout.window_incident, null)
+            adapter = EventListAdapter(contexts, eventList)
+            /**初始化LayoutMannager*/
+            virtualLayoutManager = VirtualLayoutManager(contexts)
+
+            /**初始化适配器*/
+            delegateAdapter = DelegateAdapter(virtualLayoutManager)
+            delegateAdapter.addAdapter(adapter)
+
+            val windowIncidentBinding: WindowIncidentBinding = DataBindingUtil.bind(view!!)!!
+
+            windowIncidentBinding.rvEventList.layoutManager = virtualLayoutManager
+            windowIncidentBinding.rvEventList.adapter = delegateAdapter
+
+            /*var alphaAnimation = AlphaAnimation(1.0f, 0.1f)
+            alphaAnimation.duration = 600
+            alphaAnimation.interpolator = LinearInterpolator()
+            alphaAnimation.repeatCount = Animation.INFINITE //表示重复多次
+            alphaAnimation.repeatMode = Animation.REVERSE //表示动画结束后,反过来再执行;RESTART表示从头开始,REVERSE表示从末尾倒播
+            windowIncidentBinding.viewEventTitle.startAnimation(alphaAnimation)*/
+
+            //添加mFloatLayout
+            mWindowManager!!.addView(view, wmParams)
+
+        } else {
+            adapter!!.updateData(eventList)
+        }
+
+        adapter!!.setOnItemClickListener { data, position ->
+            if (SettingConfig.getTtsMode(contexts) == SettingConfig.TTS_ON) {
+                SpeechUtil.getInstance().stopSpeak(true)
+            } else {
+                RingPlayHelper.stopRingTone()
+            }
+
+            UdpSendUtil.getInstance().sendUdpData(UdpIndex.SOS_CANCEL, data.fromFrameName, "", "", "", data.fromMacAddr, "", "", "", "", "", Constant.DEVICE_ID, data.fromDeviceId, Constant.PART_ID)
+            //LedHelper.updateLedInfo(interactionData, false, true)
+
+            eventList.remove(data)
+            if (eventList.size > 0) {
+                adapter!!.updateData(eventList)
+            } else {
+                release()
+            }
+        }
+
+    }
+
+
+    /**
+     * 删除窗口
+     */
+    fun release() {
+        DeviceChannel.calling = false
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if (view != null) {
+            //移除悬浮窗口
+            mWindowManager?.removeView(view)
+        }
+        eventList.clear()
+        mWindowManager = null
+        view = null
+    }
+
+    fun declineItem(item: UdpItem) {
+        if (SettingConfig.getTtsMode(contexts) != SettingConfig.TTS_ON) {
+            RingPlayHelper.stopRingTone()
+        }
+        if(view==null){
+            return
+        }else if (eventList.size==0){
+            release()
+        }else{
+            val event =  eventList.stream().filter{
+                it.fromMacAddr.equals(item.fromMacAddr)
+            }.findFirst().orElse(null)
+
+            if(event!=null){
+                eventList.remove(event)
+                if (eventList.size > 0) {
+                    adapter!!.updateData(eventList)
+                } else {
+                    release()
+                }
+            }
+
+        }
+    }
+
+
+    class EventListAdapter(var context: Context, var data: ArrayList<UdpItem>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterEventListItemBinding>, UdpItem>() {
+        /**
+         * 数据提供者
+         */
+        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)
+        }
+
+        fun updateData(data: ArrayList<UdpItem>) {
+            this.data = data
+            notifyDataSetChanged()
+        }
+
+        override fun onCreateViewHolder(
+            p0: ViewGroup,
+            p1: Int
+        ): BaseRecyclerViewHolder<AdapterEventListItemBinding> {
+            return BaseRecyclerViewHolder.build(p0, R.layout.adapter_event_list_item)
+        }
+
+        override fun onBindViewHolder(
+            p0: BaseRecyclerViewHolder<AdapterEventListItemBinding>,
+            p1: Int
+        ) {
+            p0.bind { binding ->
+                val room = getItem(p1).fromFrameName
+                binding.tvAlarmIndex.text = "" + (p1+1)
+                binding.handleImagev.setText(R.string.event_todo)
+                binding.handleImagev.setBackgroundResource(R.drawable.sp_event_unhandled_bg)
+                val text = BaseApplication.appContext.getString(R.string.sos_call_speech, room)
+                binding.incidentTv.text = text
+            }
+        }
+    }
+
+}

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

@@ -41,6 +41,7 @@
             android:layout_height="wrap_content"
             android:paddingLeft="8dp"
             android:paddingTop="4dp"
+            android:textColor="#F78B8F"
             android:textSize="18sp"
             android:text="--:--"/>
     </LinearLayout>

+ 55 - 0
android_host/src/main/res/layout/adapter_offline_home_frame_bed_lay.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <LinearLayout
+        android:layout_width="142dp"
+        android:layout_height="128dp"
+        android:layout_marginRight="@dimen/d5"
+        android:background="@drawable/shape_host_bed_bg"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/d30"
+            android:background="@drawable/sp_home_bed_item_t_bg">
+
+            <TextView
+                android:id="@+id/bed_lay_f_number"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="@dimen/d10"
+                android:text="-房"
+                android:textColor="@color/white"
+                android:textSize="@dimen/font_size_16"
+                android:textStyle="bold" />
+
+        </RelativeLayout>
+
+        <com.wdkl.ncs.android.lib.widget.MarqueeTextView
+            android:id="@+id/bed_lay_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/d5"
+            android:ellipsize="marquee"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            android:gravity="center"
+            android:marqueeRepeatLimit="marquee_forever"
+            android:singleLine="true"
+            android:text="---"
+            android:textColor="@color/black"
+            android:textSize="@dimen/font_size_18"
+            android:textStyle="bold" />
+
+        <ImageView
+            android:id="@+id/iv_call_bed"
+            android:layout_width="44dp"
+            android:layout_height="44dp"
+            android:layout_marginTop="12dp"
+            android:src="@drawable/selector_call_answer"/>
+
+    </LinearLayout>
+</layout>

+ 1 - 1
android_host/src/main/res/layout/view_title_layout.xml

@@ -188,7 +188,7 @@
             android:layout_gravity="center_vertical"
             android:layout_marginLeft="4dp"
             android:layout_marginRight="4dp"
-            android:src="@mipmap/ic_tcp_fail"/>
+            android:src="@mipmap/ic_tcp_nor"/>
 
         <ImageView
             android:id="@+id/view_title_layout_iv_ethernet"

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

@@ -87,7 +87,7 @@
     <string name="call_video_call">Video call</string>
     <string name="call_in_calling">Calling</string>
     <string name="call_connecting">Connecting…</string>
-    <string name="call_in_call">In call…</string>
+    <string name="call_in_call">Connected…</string>
     <string name="call_disconnect">Disconnect</string>
     <string name="call_error">Call error</string>
     <string name="call_reject">Call reject</string>

+ 1 - 1
middleware/build.gradle

@@ -89,7 +89,7 @@ dependencies {
 
 greendao {
     //这里是数据库版本,需要比原来的大
-    schemaVersion 1
+    schemaVersion 2
     // 生成数据库文件的目录
     targetGenDir 'src/main/code'
     // 生成的数据库相关文件的包名

+ 2 - 2
middleware/src/main/code/com/wdkl/greendao/gen/DaoMaster.java

@@ -14,10 +14,10 @@ import org.greenrobot.greendao.identityscope.IdentityScopeType;
 
 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
 /**
- * Master of DAO (schema version 1): knows all DAOs.
+ * Master of DAO (schema version 2): knows all DAOs.
  */
 public class DaoMaster extends AbstractDaoMaster {
-    public static final int SCHEMA_VERSION = 1;
+    public static final int SCHEMA_VERSION = 2;
 
     /** Creates underlying database table using DAOs. */
     public static void createAllTables(Database db, boolean ifNotExists) {

+ 56 - 28
middleware/src/main/code/com/wdkl/greendao/gen/FrameBedBeanDao.java

@@ -26,11 +26,13 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
     public static class Properties {
         public final static Property Id = new Property(0, Integer.class, "id", false, "ID");
         public final static Property PartId = new Property(1, Integer.class, "partId", false, "PART_ID");
-        public final static Property FullName = new Property(2, String.class, "fullName", false, "FULL_NAME");
+        public final static Property FrameName = new Property(2, String.class, "frameName", false, "FRAME_NAME");
         public final static Property CustomerName = new Property(3, String.class, "customerName", false, "CUSTOMER_NAME");
-        public final static Property DeviceSipId = new Property(4, String.class, "deviceSipId", false, "DEVICE_SIP_ID");
-        public final static Property BedDeviceId = new Property(5, Integer.class, "bedDeviceId", false, "BED_DEVICE_ID");
-        public final static Property BedDeviceMac = new Property(6, String.class, "bedDeviceMac", false, "BED_DEVICE_MAC");
+        public final static Property DoctorName = new Property(4, String.class, "doctorName", false, "DOCTOR_NAME");
+        public final static Property NurseName = new Property(5, String.class, "nurseName", false, "NURSE_NAME");
+        public final static Property DeviceSipId = new Property(6, String.class, "deviceSipId", false, "DEVICE_SIP_ID");
+        public final static Property BedDeviceId = new Property(7, Integer.class, "bedDeviceId", false, "BED_DEVICE_ID");
+        public final static Property BedDeviceMac = new Property(8, String.class, "bedDeviceMac", false, "BED_DEVICE_MAC");
     }
 
 
@@ -48,11 +50,13 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
         db.execSQL("CREATE TABLE " + constraint + "\"FRAME_BED_BEAN\" (" + //
                 "\"ID\" INTEGER," + // 0: id
                 "\"PART_ID\" INTEGER," + // 1: partId
-                "\"FULL_NAME\" TEXT," + // 2: fullName
+                "\"FRAME_NAME\" TEXT," + // 2: frameName
                 "\"CUSTOMER_NAME\" TEXT," + // 3: customerName
-                "\"DEVICE_SIP_ID\" TEXT," + // 4: deviceSipId
-                "\"BED_DEVICE_ID\" INTEGER," + // 5: bedDeviceId
-                "\"BED_DEVICE_MAC\" TEXT);"); // 6: bedDeviceMac
+                "\"DOCTOR_NAME\" TEXT," + // 4: doctorName
+                "\"NURSE_NAME\" TEXT," + // 5: nurseName
+                "\"DEVICE_SIP_ID\" TEXT," + // 6: deviceSipId
+                "\"BED_DEVICE_ID\" INTEGER," + // 7: bedDeviceId
+                "\"BED_DEVICE_MAC\" TEXT);"); // 8: bedDeviceMac
     }
 
     /** Drops the underlying database table. */
@@ -75,9 +79,9 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
             stmt.bindLong(2, partId);
         }
  
-        String fullName = entity.getFullName();
-        if (fullName != null) {
-            stmt.bindString(3, fullName);
+        String frameName = entity.getFrameName();
+        if (frameName != null) {
+            stmt.bindString(3, frameName);
         }
  
         String customerName = entity.getCustomerName();
@@ -85,19 +89,29 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
             stmt.bindString(4, customerName);
         }
  
+        String doctorName = entity.getDoctorName();
+        if (doctorName != null) {
+            stmt.bindString(5, doctorName);
+        }
+ 
+        String nurseName = entity.getNurseName();
+        if (nurseName != null) {
+            stmt.bindString(6, nurseName);
+        }
+ 
         String deviceSipId = entity.getDeviceSipId();
         if (deviceSipId != null) {
-            stmt.bindString(5, deviceSipId);
+            stmt.bindString(7, deviceSipId);
         }
  
         Integer bedDeviceId = entity.getBedDeviceId();
         if (bedDeviceId != null) {
-            stmt.bindLong(6, bedDeviceId);
+            stmt.bindLong(8, bedDeviceId);
         }
  
         String bedDeviceMac = entity.getBedDeviceMac();
         if (bedDeviceMac != null) {
-            stmt.bindString(7, bedDeviceMac);
+            stmt.bindString(9, bedDeviceMac);
         }
     }
 
@@ -115,9 +129,9 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
             stmt.bindLong(2, partId);
         }
  
-        String fullName = entity.getFullName();
-        if (fullName != null) {
-            stmt.bindString(3, fullName);
+        String frameName = entity.getFrameName();
+        if (frameName != null) {
+            stmt.bindString(3, frameName);
         }
  
         String customerName = entity.getCustomerName();
@@ -125,19 +139,29 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
             stmt.bindString(4, customerName);
         }
  
+        String doctorName = entity.getDoctorName();
+        if (doctorName != null) {
+            stmt.bindString(5, doctorName);
+        }
+ 
+        String nurseName = entity.getNurseName();
+        if (nurseName != null) {
+            stmt.bindString(6, nurseName);
+        }
+ 
         String deviceSipId = entity.getDeviceSipId();
         if (deviceSipId != null) {
-            stmt.bindString(5, deviceSipId);
+            stmt.bindString(7, deviceSipId);
         }
  
         Integer bedDeviceId = entity.getBedDeviceId();
         if (bedDeviceId != null) {
-            stmt.bindLong(6, bedDeviceId);
+            stmt.bindLong(8, bedDeviceId);
         }
  
         String bedDeviceMac = entity.getBedDeviceMac();
         if (bedDeviceMac != null) {
-            stmt.bindString(7, bedDeviceMac);
+            stmt.bindString(9, bedDeviceMac);
         }
     }
 
@@ -151,11 +175,13 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
         FrameBedBean entity = new FrameBedBean( //
             cursor.isNull(offset + 0) ? null : cursor.getInt(offset + 0), // id
             cursor.isNull(offset + 1) ? null : cursor.getInt(offset + 1), // partId
-            cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // fullName
+            cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // frameName
             cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // customerName
-            cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // deviceSipId
-            cursor.isNull(offset + 5) ? null : cursor.getInt(offset + 5), // bedDeviceId
-            cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6) // bedDeviceMac
+            cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // doctorName
+            cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5), // nurseName
+            cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6), // deviceSipId
+            cursor.isNull(offset + 7) ? null : cursor.getInt(offset + 7), // bedDeviceId
+            cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8) // bedDeviceMac
         );
         return entity;
     }
@@ -164,11 +190,13 @@ public class FrameBedBeanDao extends AbstractDao<FrameBedBean, Void> {
     public void readEntity(Cursor cursor, FrameBedBean entity, int offset) {
         entity.setId(cursor.isNull(offset + 0) ? null : cursor.getInt(offset + 0));
         entity.setPartId(cursor.isNull(offset + 1) ? null : cursor.getInt(offset + 1));
-        entity.setFullName(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
+        entity.setFrameName(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
         entity.setCustomerName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3));
-        entity.setDeviceSipId(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));
-        entity.setBedDeviceId(cursor.isNull(offset + 5) ? null : cursor.getInt(offset + 5));
-        entity.setBedDeviceMac(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));
+        entity.setDoctorName(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));
+        entity.setNurseName(cursor.isNull(offset + 5) ? null : cursor.getString(offset + 5));
+        entity.setDeviceSipId(cursor.isNull(offset + 6) ? null : cursor.getString(offset + 6));
+        entity.setBedDeviceId(cursor.isNull(offset + 7) ? null : cursor.getInt(offset + 7));
+        entity.setBedDeviceMac(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8));
      }
     
     @Override

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

@@ -8,7 +8,9 @@ import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum;
 import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class Constant {
 
@@ -125,8 +127,9 @@ public class Constant {
     public static int Window_size = 1;
 
     //呼叫参数
-    public final static String calling_type = "calling_type";  //0=webrt,1=sip,2=远程探视
+    public final static String calling_type = "calling_type";  //0=webrtc,1=sip,2=远程探视
     public final static String calling_state = "calling_state";
+    public final static String calling_offline = "calling_offline";
     public final static String bc_play = "bc_play";
     public final static String tcp_model = "tcp_model";
     public final static String tcp_tid = "tcp_tid";
@@ -325,6 +328,10 @@ public class Constant {
     public static int RTC_port = 0;
     public static String union_id = "";
 
+    public static Map<String, Long> offlineMap = new HashMap<>();
+    public static Map<String, Long> onlineMap = new HashMap<>();
+
+
     //广播
     public static boolean gstreamer_init = false;
     public static int selectBcId = 0;

+ 35 - 11
middleware/src/main/code/com/wdkl/ncs/android/middleware/dao/entity/FrameBedBean.java

@@ -17,12 +17,18 @@ public class FrameBedBean {
     @Column(name = "part_id")
     private Integer partId;
 
-    @Column(name = "full_name")
-    private String fullName;
+    @Column(name = "frame_name")
+    private String frameName;
 
     @Column(name = "customer_name")
     private String customerName;
 
+    @Column(name = "doctor_name")
+    private String doctorName;
+
+    @Column(name = "nurse_name")
+    private String nurseName;
+
     @Column(name = "device_sip_id")
     private String deviceSipId;
 
@@ -32,14 +38,16 @@ public class FrameBedBean {
     @Column(name = "device_mac")
     private String bedDeviceMac;
 
-    @Generated(hash = 568436496)
-    public FrameBedBean(Integer id, Integer partId, String fullName,
-            String customerName, String deviceSipId, Integer bedDeviceId,
-            String bedDeviceMac) {
+    @Generated(hash = 287165114)
+    public FrameBedBean(Integer id, Integer partId, String frameName,
+            String customerName, String doctorName, String nurseName,
+            String deviceSipId, Integer bedDeviceId, String bedDeviceMac) {
         this.id = id;
         this.partId = partId;
-        this.fullName = fullName;
+        this.frameName = frameName;
         this.customerName = customerName;
+        this.doctorName = doctorName;
+        this.nurseName = nurseName;
         this.deviceSipId = deviceSipId;
         this.bedDeviceId = bedDeviceId;
         this.bedDeviceMac = bedDeviceMac;
@@ -65,12 +73,28 @@ public class FrameBedBean {
         this.partId = partId;
     }
 
-    public String getFullName() {
-        return this.fullName;
+    public String getFrameName() {
+        return frameName;
+    }
+
+    public void setFrameName(String frameName) {
+        this.frameName = frameName;
+    }
+
+    public String getDoctorName() {
+        return doctorName;
+    }
+
+    public void setDoctorName(String doctorName) {
+        this.doctorName = doctorName;
+    }
+
+    public String getNurseName() {
+        return nurseName;
     }
 
-    public void setFullName(String fullName) {
-        this.fullName = fullName;
+    public void setNurseName(String nurseName) {
+        this.nurseName = nurseName;
     }
 
     public String getDeviceSipId() {

+ 36 - 2
middleware/src/main/code/com/wdkl/ncs/android/middleware/entity/CallingItem.java

@@ -2,6 +2,7 @@ package com.wdkl.ncs.android.middleware.entity;
 
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.udp2.UdpItem;
 
 public class CallingItem {
     private long startTime;
@@ -9,11 +10,16 @@ public class CallingItem {
     private InteractionVO interactionVO;
     private TcpAction action;
 
-    public CallingItem(String tid, long startTime, InteractionVO interactionVO, TcpAction action) {
-        this.tid = tid;
+    private int type; //0=tcp,1=udp
+    private UdpItem udpItem;
+
+    public CallingItem(long startTime, String tid, InteractionVO interactionVO, TcpAction action, int type, UdpItem udpItem) {
         this.startTime = startTime;
+        this.tid = tid;
         this.interactionVO = interactionVO;
         this.action = action;
+        this.type = type;
+        this.udpItem = udpItem;
     }
 
     public long getStartTime() {
@@ -47,4 +53,32 @@ public class CallingItem {
     public void setAction(TcpAction action) {
         this.action = action;
     }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public UdpItem getUdpItem() {
+        return udpItem;
+    }
+
+    public void setUdpItem(UdpItem udpItem) {
+        this.udpItem = udpItem;
+    }
+
+    @Override
+    public String toString() {
+        return "CallingItem{" +
+                "startTime=" + startTime +
+                ", tid='" + tid + '\'' +
+                ", interactionVO=" + interactionVO +
+                ", action=" + action +
+                ", type=" + type +
+                ", udpItem=" + udpItem +
+                '}';
+    }
 }

+ 10 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/NurseDeviceInfoVO.java

@@ -30,6 +30,8 @@ public class NurseDeviceInfoVO extends DeviceDO {
     @ApiModelProperty(value = "科室显示名称(科室全称字段)", required = false)
     private String partDisplay;
 
+    private Boolean hostDeviceMain;
+
     public Integer getHospitalId() {
         return hospitalId;
     }
@@ -61,4 +63,12 @@ public class NurseDeviceInfoVO extends DeviceDO {
     public void setPartDisplay(String partDisplay) {
         this.partDisplay = partDisplay;
     }
+
+    public Boolean getHostDeviceMain() {
+        return hostDeviceMain;
+    }
+
+    public void setHostDeviceMain(Boolean hostDeviceMain) {
+        this.hostDeviceMain = hostDeviceMain;
+    }
 }

+ 1 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpHelper.java

@@ -27,6 +27,7 @@ public class UdpHelper implements Runnable {
         // 接收的字节大小,客户端发送的数据不能超过这个大小
         byte[] message = new byte[1400];
         try {
+            Log.d("UDP_Helper", "Start udp listener...");
             // 建立Socket连接
             DatagramSocket datagramSocket = new DatagramSocket(udpPort);
             datagramSocket.setBroadcast(true);

+ 4 - 2
middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpIndex.java

@@ -1,8 +1,10 @@
 package com.wdkl.ncs.android.middleware.udp2;
 
 public class UdpIndex {
-    public static final String SERVER_MODE_OFFLINE = "server_mode_offline";
-    public static final String SERVER_MODE_ONLINE = "server_mode_online";
+    public static final String SERVER_MODE_OFFLINE = "SERVER_MODE_OFFLINE";
+    public static final String SERVER_MODE_ONLINE = "SERVER_MODE_ONLINE";
+    public static final String DEVICE_OFFLINE = "DEVICE_OFFLINE";
+    public static final String DEVICE_ONLINE = "DEVICE_ONLINE";
 
     //分机呼叫主机
     public static final String BED_CALL_OUT = "BED_CALL_OUT";

+ 87 - 36
middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpItem.java

@@ -2,45 +2,42 @@ package com.wdkl.ncs.android.middleware.udp2;
 
 public class UdpItem {
     private String index;
-    private String frameName;
+    private String fromFrameName;
+    private String toFrameName;
     private String patientName;
-    private String uartAddr;
-    private String myIpAddr;
-    private String targetIpAddr;
+    private String fromMacAddr;
+    private String toMacAddr;
+    private String fromIpAddr;
+    private String toIpAddr;
+    private String targetSipId;
+    private String targetSipIp;
     private String data;
-    private int deviceId;
+    private int fromDeviceId;
+    private int toDeviceId;
     private int partId;
 
     public String getIndex() {
-        return null == index ? "null" : index;
+        return index;
     }
 
     public void setIndex(String index) {
         this.index = index;
     }
 
-    public String getFrameName() {
-        return frameName;
+    public String getFromFrameName() {
+        return fromFrameName;
     }
 
-    public void setFrameName(String frameName) {
-        this.frameName = frameName;
+    public void setFromFrameName(String fromFrameName) {
+        this.fromFrameName = fromFrameName;
     }
 
-    public String getMyIpAddr() {
-        return myIpAddr;
+    public String getToFrameName() {
+        return toFrameName;
     }
 
-    public void setMyIpAddr(String myAddr) {
-        this.myIpAddr = myAddr;
-    }
-
-    public String getTargetIpAddr() {
-        return targetIpAddr;
-    }
-
-    public void setTargetIpAddr(String targetAddr) {
-        this.targetIpAddr = targetAddr;
+    public void setToFrameName(String toFrameName) {
+        this.toFrameName = toFrameName;
     }
 
     public String getPatientName() {
@@ -51,12 +48,52 @@ public class UdpItem {
         this.patientName = patientName;
     }
 
-    public String getUartAddr() {
-        return uartAddr;
+    public String getFromMacAddr() {
+        return fromMacAddr;
+    }
+
+    public void setFromMacAddr(String fromMacAddr) {
+        this.fromMacAddr = fromMacAddr;
+    }
+
+    public String getToMacAddr() {
+        return toMacAddr;
+    }
+
+    public void setToMacAddr(String toMacAddr) {
+        this.toMacAddr = toMacAddr;
+    }
+
+    public String getFromIpAddr() {
+        return fromIpAddr;
+    }
+
+    public void setFromIpAddr(String fromIpAddr) {
+        this.fromIpAddr = fromIpAddr;
     }
 
-    public void setUartAddr(String uartAddr) {
-        this.uartAddr = uartAddr;
+    public String getToIpAddr() {
+        return toIpAddr;
+    }
+
+    public void setToIpAddr(String toIpAddr) {
+        this.toIpAddr = toIpAddr;
+    }
+
+    public String getTargetSipId() {
+        return targetSipId;
+    }
+
+    public void setTargetSipId(String targetSipId) {
+        this.targetSipId = targetSipId;
+    }
+
+    public String getTargetSipIp() {
+        return targetSipIp;
+    }
+
+    public void setTargetSipIp(String targetSipIp) {
+        this.targetSipIp = targetSipIp;
     }
 
     public String getData() {
@@ -67,12 +104,20 @@ public class UdpItem {
         this.data = data;
     }
 
-    public int getDeviceId() {
-        return deviceId;
+    public int getFromDeviceId() {
+        return fromDeviceId;
+    }
+
+    public void setFromDeviceId(int fromDeviceId) {
+        this.fromDeviceId = fromDeviceId;
+    }
+
+    public int getToDeviceId() {
+        return toDeviceId;
     }
 
-    public void setDeviceId(int deviceId) {
-        this.deviceId = deviceId;
+    public void setToDeviceId(int toDeviceId) {
+        this.toDeviceId = toDeviceId;
     }
 
     public int getPartId() {
@@ -86,14 +131,20 @@ public class UdpItem {
     @Override
     public String toString() {
         return "UdpItem{" +
-                "frameName='" + frameName + '\'' +
+                "index='" + index + '\'' +
+                ", fromFrameName='" + fromFrameName + '\'' +
+                ", toFrameName='" + toFrameName + '\'' +
                 ", patientName='" + patientName + '\'' +
-                ", uartAddr='" + uartAddr + '\'' +
-                ", myAddr='" + myIpAddr + '\'' +
-                ", targetAddr='" + targetIpAddr + '\'' +
+                ", fromMacAddr='" + fromMacAddr + '\'' +
+                ", toMacAddr='" + toMacAddr + '\'' +
+                ", fromIpAddr='" + fromIpAddr + '\'' +
+                ", toIpAddr='" + toIpAddr + '\'' +
+                ", targetSipId='" + targetSipId + '\'' +
+                ", targetSipIp='" + targetSipIp + '\'' +
                 ", data='" + data + '\'' +
-                ", deviceId='" + deviceId + '\'' +
-                ", partId='" + partId + '\'' +
+                ", fromDeviceId=" + fromDeviceId +
+                ", toDeviceId=" + toDeviceId +
+                ", partId=" + partId +
                 '}';
     }
 }

+ 27 - 11
middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpReceivedUtil.java

@@ -3,7 +3,7 @@ package com.wdkl.ncs.android.middleware.udp2;
 import android.util.Log;
 
 import com.wdkl.ncs.android.middleware.common.Constant;
-import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
 
 import org.greenrobot.eventbus.EventBus;
 
@@ -23,32 +23,48 @@ public class UdpReceivedUtil {
             final String[] data = udpMsg.split(";");
 
             //判断udp消息长度是否符合条件,并且不是本机发出的
-            if (data.length < 9) {
+            if (data.length < 14) {
                 return;
-            } else if (data[7].equals(Integer.toString(Constant.DEVICE_ID))) {
+            } else if (data[11].equals(Integer.toString(Constant.DEVICE_ID))) {
                 //本机发送的数据不处理
                 return;
-            } else if (!data[8].equals(Integer.toString(Constant.PART_ID))) {
+            } else if (!data[13].equals(Integer.toString(Constant.PART_ID))) {
                 //非本科室数据不处理
                 return;
             }
 
             switch (data[0]) {
+                case UdpIndex.SERVER_MODE_OFFLINE:
+                case UdpIndex.SERVER_MODE_ONLINE:
+                case UdpIndex.DEVICE_OFFLINE:
+                case UdpIndex.DEVICE_ONLINE:
                 case UdpIndex.BED_CALL_OUT:
                 case UdpIndex.BED_CALL_CANCEL:
+                case UdpIndex.BED_CALL_ACCEPT:
+                case UdpIndex.BED_CALL_REJECT:
                 case UdpIndex.BED_CALL_END:
                 case UdpIndex.CALL_CALLING:
+                case UdpIndex.HOST_CALL_BED:
+                case UdpIndex.HOST_CALL_CANCEL:
                 case UdpIndex.HOST_CALL_ACCEPT:
                 case UdpIndex.HOST_CALL_REJECT:
                 case UdpIndex.SOS_CALL:
+                case UdpIndex.SOS_CANCEL:
                     UdpItem udpItem = new UdpItem();
                     udpItem.setIndex(data[0]);
-                    udpItem.setFrameName(data[1]);
-                    udpItem.setUartAddr(data[3]);
-                    udpItem.setTargetIpAddr(data[4]);
-                    udpItem.setData(data[6]);
-                    udpItem.setDeviceId(Integer.parseInt(data[7]));
-                    udpItem.setPartId(Integer.parseInt(data[8]));
+                    udpItem.setFromFrameName(data[1]);
+                    udpItem.setToFrameName(data[2]);
+                    udpItem.setPatientName(data[3]);
+                    udpItem.setFromMacAddr(data[4]);
+                    udpItem.setToMacAddr(data[5]);
+                    udpItem.setFromIpAddr(data[6]);
+                    udpItem.setToIpAddr(data[7]);
+                    udpItem.setTargetSipId(data[8]);
+                    udpItem.setTargetSipIp(data[9]);
+                    udpItem.setData(data[10]);
+                    udpItem.setFromDeviceId(Integer.parseInt(data[11]));
+                    udpItem.setToDeviceId(Integer.parseInt(data[12]));
+                    udpItem.setPartId(Integer.parseInt(data[13]));
                     EventBus.getDefault().post(new MessageEvent(udpItem, Constant.EVENT_UDP));
                     break;
             }
@@ -77,4 +93,4 @@ public class UdpReceivedUtil {
         }
         return source;
     }
-}
+}

+ 30 - 14
middleware/src/main/code/com/wdkl/ncs/android/middleware/udp2/UdpSendUtil.java

@@ -26,24 +26,40 @@ public class UdpSendUtil {
     public static class SendUdpThread implements Runnable {
         @Override
         public void run() {
-            UdpHelper.send("$" + strUdp + "#");
+            try {
+                UdpHelper.send("$" + strUdp + "#");
+            } catch (Exception e) {
+                //
+            }
         }
     }
 
-    public synchronized void sendUdpData(String index, String frameName, String patientName, String macAddr, String myIpAddr, String targetIpAddr, String data, String deviceId, String partId) {
-        strUdp = index +
-                regex + frameName +
-                regex + patientName +
-                regex + macAddr +
-                regex + myIpAddr +
-                regex + targetIpAddr +
-                regex + data +
-                regex + deviceId +
-                regex + partId;
-        if (sendUdpThread == null) {
-            sendUdpThread = new SendUdpThread();
+    public synchronized void sendUdpData(String index, String fromFrameName, String toFrameName, String patientName,
+                                         String fromMacAddr, String toMacAddr, String fromIpAddr, String toIpAddr,
+                                         String sipId, String sipIP, String data,
+                                         int fromDeviceId, int toDeviceId, int partId) {
+        try {
+            strUdp = index +
+                    regex + fromFrameName +
+                    regex + toFrameName +
+                    regex + patientName +
+                    regex + fromMacAddr +
+                    regex + toMacAddr +
+                    regex + fromIpAddr +
+                    regex + toIpAddr +
+                    regex + sipId +
+                    regex + sipIP +
+                    regex + data +
+                    regex + fromDeviceId +
+                    regex + toDeviceId +
+                    regex + partId;
+            if (sendUdpThread == null) {
+                sendUdpThread = new SendUdpThread();
+            }
+            threadPool.execute(sendUdpThread);
+        } catch (Exception e) {
+            e.printStackTrace();
         }
-        threadPool.execute(sendUdpThread);
     }
 
 }

+ 0 - 169
middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/AudioRouteUtils.kt

@@ -1,169 +0,0 @@
-package com.wdkl.ncs.android.middleware.utils;
-
-import org.linphone.core.AudioDevice
-import org.linphone.core.Call
-import org.linphone.core.Core
-import org.linphone.core.tools.Log
-
-class AudioRouteUtils {
-    companion object {
-        private fun applyAudioRouteChange(
-            core: Core,
-            call: Call?,
-            types: List<AudioDevice.Type>,
-            output: Boolean = true
-        ) {
-            val currentCall = if (core.callsNb > 0) {
-                call ?: core.currentCall ?: core.calls[0]
-            } else {
-                Log.w("[Audio Route Helper] No call found, setting audio route on Core")
-                null
-            }
-            val conference = core.conference
-            val capability = if (output)
-                AudioDevice.Capabilities.CapabilityPlay
-            else
-                AudioDevice.Capabilities.CapabilityRecord
-            val preferredDriver = if (output) {
-                core.defaultOutputAudioDevice?.driverName
-            } else {
-                core.defaultInputAudioDevice?.driverName
-            }
-
-            val extendedAudioDevices = core.extendedAudioDevices
-            Log.i("[Audio Route Helper] Looking for an ${if (output) "output" else "input"} audio device with capability [$capability], driver name [$preferredDriver] and type [$types] in extended audio devices list (size ${extendedAudioDevices.size})")
-            val foundAudioDevice = extendedAudioDevices.find {
-                it.driverName == preferredDriver && types.contains(it.type) && it.hasCapability(capability)
-            }
-            val audioDevice = if (foundAudioDevice == null) {
-                Log.w("[Audio Route Helper] Failed to find an audio device with capability [$capability], driver name [$preferredDriver] and type [$types]")
-                extendedAudioDevices.find {
-                    types.contains(it.type) && it.hasCapability(capability)
-                }
-            } else {
-                foundAudioDevice
-            }
-
-            if (audioDevice == null) {
-                Log.e("[Audio Route Helper] Couldn't find audio device with capability [$capability] and type [$types]")
-                for (device in extendedAudioDevices) {
-                    // TODO: switch to debug?
-                    Log.i("[Audio Route Helper] Extended audio device: [${device.deviceName} (${device.driverName}) ${device.type} / ${device.capabilities}]")
-                }
-                return
-            }
-            if (conference != null && conference.isIn) {
-                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing conference audio to it")
-                if (output) conference.outputAudioDevice = audioDevice
-                else conference.inputAudioDevice = audioDevice
-            } else if (currentCall != null) {
-                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing call audio to it")
-                if (output) currentCall.outputAudioDevice = audioDevice
-                else currentCall.inputAudioDevice = audioDevice
-            } else {
-                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], changing core default audio device")
-                if (output) core.outputAudioDevice = audioDevice
-                else core.inputAudioDevice = audioDevice
-            }
-        }
-
-        private fun changeCaptureDeviceToMatchAudioRoute(core: Core, call: Call?, types: List<AudioDevice.Type>) {
-            when (types.first()) {
-                AudioDevice.Type.Bluetooth -> {
-                    if (isBluetoothAudioRecorderAvailable(core)) {
-                        Log.i("[Audio Route Helper] Bluetooth device is able to record audio, also change input audio device")
-                        applyAudioRouteChange(core, call, arrayListOf(AudioDevice.Type.Bluetooth), false)
-                    }
-                }
-                AudioDevice.Type.Headset, AudioDevice.Type.Headphones -> {
-                    if (isHeadsetAudioRecorderAvailable(core)) {
-                        Log.i("[Audio Route Helper] Headphones/Headset device is able to record audio, also change input audio device")
-                        applyAudioRouteChange(core, call, (arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset)), false)
-                    }
-                }
-                AudioDevice.Type.Earpiece, AudioDevice.Type.Speaker -> {
-                    Log.i("[Audio Route Helper] Audio route requested to Earpiece or Speaker, setting input to Microphone")
-                    applyAudioRouteChange(core, call, (arrayListOf(AudioDevice.Type.Microphone)), false)
-                }
-                else -> {
-                    Log.w("[Audio Route Helper] Unexpected audio device type: ${types.first()}")
-                }
-            }
-        }
-
-        private fun routeAudioTo(
-            core: Core,
-            call: Call?,
-            types: List<AudioDevice.Type>,
-            skipTelecom: Boolean = false
-        ) {
-            val currentCall = call ?: core.currentCall ?: core.calls.firstOrNull()
-
-                if (currentCall != null) {
-                    Log.i("[Audio Route Helper] Telecom Helper & matching connection found, dispatching audio route change through it")
-                    // We will be called here again by NativeCallWrapper.onCallAudioStateChanged()
-                    // but this time with skipTelecom = true
-                    //if (!Compatibility.changeAudioRouteForTelecomManager(connection, route)) {
-                        Log.w("[Audio Route Helper] Connection is already using this route internally, make the change!")
-                        applyAudioRouteChange(core, currentCall, types)
-                        changeCaptureDeviceToMatchAudioRoute(core, currentCall, types)
-                    //}
-                }
-
-        }
-
-        fun routeAudioToEarpiece(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
-            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Earpiece), skipTelecom)
-        }
-
-        fun routeAudioToSpeaker(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
-            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Speaker), skipTelecom)
-        }
-
-        fun routeAudioToBluetooth(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
-            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Bluetooth), skipTelecom)
-        }
-
-        fun routeAudioToHeadset(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
-            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset), skipTelecom)
-        }
-
-
-
-        fun isBluetoothAudioRouteAvailable(core: Core): Boolean {
-            for (audioDevice in core.audioDevices) {
-                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
-                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
-                ) {
-                    Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName} (${audioDevice.driverName})]")
-                    return true
-                }
-            }
-            return false
-        }
-
-        private fun isBluetoothAudioRecorderAvailable(core: Core): Boolean {
-            for (audioDevice in core.audioDevices) {
-                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
-                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
-                ) {
-                    Log.i("[Audio Route Helper] Found bluetooth audio recorder [${audioDevice.deviceName} (${audioDevice.driverName})]")
-                    return true
-                }
-            }
-            return false
-        }
-
-        private fun isHeadsetAudioRecorderAvailable(core: Core): Boolean {
-            for (audioDevice in core.audioDevices) {
-                if ((audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) &&
-                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
-                ) {
-                    Log.i("[Audio Route Helper] Found headset/headphones audio recorder [${audioDevice.deviceName} (${audioDevice.driverName})]")
-                    return true
-                }
-            }
-            return false
-        }
-    }
-}

+ 1 - 1
middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/CommonUtils.java

@@ -64,7 +64,7 @@ public class CommonUtils {
         if (data != null) {
             bedVOS.clear();
             for (FrameBedVO bedVO : data) {
-                if (bedVO != null && bedVO.getBedDeviceId() != null && !TextUtils.isEmpty(bedVO.getCustomerName())) {
+                if (bedVO != null && bedVO.getBedDeviceId() != null) {
                     //床位有绑定设备并且有入住才加入进来
                     bedVOS.add(bedVO);
                 }

+ 0 - 41
middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/MessageEvent.kt

@@ -1,41 +0,0 @@
-package com.wdkl.ncs.android.middleware.utils
-
-class MessageEvent {
-
-    private var message: Any? = null
-    private var types: Int? =0
-
-    constructor(type: Int) {
-        types = type
-    }
-
-    companion object {
-        fun build(type: Int, msg: Any): MessageEvent{
-            var msgEvent = MessageEvent(type)
-            msgEvent.message = msg
-            return msgEvent
-        }
-    }
-
-    constructor(tcpModel: Any, type: Int) {
-        message = tcpModel
-        types = type
-    }
-
-    fun getMessage(): Any? {
-        return message
-    }
-
-    fun setMessage(msg: Any?) {
-        this.message = msg
-    }
-
-    fun getType(): Int? {
-        return types
-    }
-
-    fun setType(type: Int?) {
-        this.types = type
-    }
-
-}

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

@@ -656,4 +656,5 @@
 
     <string name="str_iptv_no_channel">No channel</string>
     <string name="str_action_error">No Activity</string>
+    <string name="disconnect_server">Disconnect with server…</string>
 </resources>

+ 13 - 1
resource/src/main/res/values-pt/strings.xml

@@ -119,6 +119,14 @@
     <string name="str_voice_msg_btn_text">Pressione para falar</string>
     <string name="str_voice_msg_record_loss">Tempo de gravação muito curto</string>
     <string name="str_voice_msg_record_cancel">Mensagem de voz cancelada</string>
+    <string name="str_empty_voice_msg">No voice message</string>
+    <string name="str_delete_tip">Delete?</string>
+    <string name="str_message_content_error">Invalid message</string>
+    <string name="str_voice_message_invalid">Invalid voice message</string>
+    <string name="str_message_save_success">Success!</string>
+    <string name="str_record_message_success">Record success!</string>
+    <string name="str_invalid_creator_name">Please select creator</string>
+
     <string name="str_voice_msg_send_success">Mensagem enviada com sucesso</string>
     <string name="str_voice_msg_send_fail">Falha ao enviar mensagem</string>
     <string name="str_voice_msg_play">Reproduzir mensagem</string>
@@ -583,7 +591,7 @@
     <string name="language_settings">Configurações de idioma</string>
     <string name="system_settings">Configurações do sistema</string>
     <string name="save_settings">Salvar configurações</string>
-
+    <string name="app_settings">APP settings</string>
 
     <string name="apk_list_1">Terminal da cabeceira</string>
     <string name="apk_list_2">Terminal da porta</string>
@@ -667,4 +675,8 @@
     <string name="mobile_msg_1">Gerenciado</string>
     <string name="mobile_msg_2">Não gerenciado</string>
     <string name="str_settings">Configurações</string>
+
+    <string name="str_iptv_no_channel">No channel</string>
+    <string name="str_action_error">No Activity</string>
+    <string name="disconnect_server">Disconnect with server…</string>
 </resources>

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

@@ -642,4 +642,5 @@
 
     <string name="str_iptv_no_channel">No channel</string>
     <string name="str_action_error">No Activity</string>
+    <string name="disconnect_server">Disconnect with server…</string>
 </resources>

+ 9 - 0
resource/src/main/res/values-zh/strings.xml

@@ -115,6 +115,14 @@
     <string name="str_voice_msg_btn_text">按住说话</string>
     <string name="str_voice_msg_record_loss">说话时间太短</string>
     <string name="str_voice_msg_record_cancel">语音留言已取消</string>
+    <string name="str_empty_voice_msg">未录制语音</string>
+    <string name="str_delete_tip">确认删除?</string>
+    <string name="str_message_content_error">留言内容为空</string>
+    <string name="str_voice_message_invalid">语音留言文件无效</string>
+    <string name="str_message_save_success">留言成功!</string>
+    <string name="str_record_message_success">语音录制成功!</string>
+    <string name="str_invalid_creator_name">请先选择创建人</string>
+
     <string name="str_voice_msg_send_success">留言发送成功</string>
     <string name="str_voice_msg_send_fail">留言发送失败</string>
     <string name="str_voice_msg_play">播放留言</string>
@@ -655,4 +663,5 @@
 
     <string name="str_iptv_no_channel">没有节目</string>
     <string name="str_action_error">找不到Activity</string>
+    <string name="disconnect_server">与服务器断开连接…</string>
 </resources>

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

@@ -657,4 +657,5 @@
 
     <string name="str_iptv_no_channel">No channel</string>
     <string name="str_action_error">No Activity</string>
+    <string name="disconnect_server">Disconnect with server…</string>
 </resources>