瀏覽代碼

#CHANGE
- 优化tcp
- 整合音视频通话
- 修复bug

weizhengliang 3 年之前
父節點
當前提交
19721a168c
共有 25 個文件被更改,包括 994 次插入249 次删除
  1. 2 0
      app/build.gradle
  2. 2 2
      bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java
  3. 21 11
      build.gradle
  4. 2 1
      callingdoor/src/main/AndroidManifest.xml
  5. 73 165
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/activity/CallingdoorActivity.kt
  6. 145 0
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/BaseCallFragment.kt
  7. 2 2
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/MainFragment.kt
  8. 452 0
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/SkyCallFragment.kt
  9. 24 0
      callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/RingPlayHelper.java
  10. 7 0
      callingdoor/src/main/res/anim/slide_down_in.xml
  11. 7 0
      callingdoor/src/main/res/anim/slide_left_in.xml
  12. 7 0
      callingdoor/src/main/res/anim/slide_right_out.xml
  13. 7 0
      callingdoor/src/main/res/anim/slide_up_out.xml
  14. 6 1
      callingdoor/src/main/res/layout/callingdoor_main_lay.xml
  15. 134 0
      callingdoor/src/main/res/layout/sky_voice_call_layout.xml
  16. 4 4
      callingdoor/src/main/res/layout/view_title_layout.xml
  17. 二進制
      callingdoor/src/main/res/raw/ring_back2.wav
  18. 6 1
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constant.java
  19. 5 5
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/FrameRoomVO.java
  20. 34 13
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClient.java
  21. 32 27
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClientHandler.java
  22. 1 7
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/channel/DeviceChannel.java
  23. 3 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/enums/TcpAction.java
  24. 17 10
      webrtc/src/main/java/com/wdkl/core/socket/SocketManager.java
  25. 1 0
      webrtc/src/main/java/com/wdkl/core/voip/AsyncPlayer.java

+ 2 - 0
app/build.gradle

@@ -31,6 +31,8 @@ android {
     buildToolsVersion build_tools_version
     aaptOptions.cruncherEnabled = false
     aaptOptions.useNewCruncher = false
+    aaptOptions.noCompress("mp3","wav")
+
     defaultConfig {
         applicationId "com.wdkl.app.ncs.callingdoor"
         minSdkVersion min_sdk_version

+ 2 - 2
bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java

@@ -103,7 +103,7 @@ public class SerialPortUtil {
                         int size = inputStream.read(buffer);
                         if (size > 0 && isOpenSerialPortUtil) {
                             data = new String(buffer, 0, size);
-                            Log.d("aaaa", "data==" + data);
+                            //Log.d("aaaa", "data==" + data);
 
                             if (systemVersionIsO()) {//安卓7.0.1 or 8.1.0 版用
                                 //addByData(data);//my do
@@ -181,7 +181,7 @@ public class SerialPortUtil {
     }
 
     public void sendData(String data) {
-        Log.d("bbbb", "data==" + data);
+        //Log.d("bbbb", "data==" + data);
         if (!StringUtils.notEmpty(data)) return;
         if (isOpenSerialPortUtil) {
             //reset data

+ 21 - 11
build.gradle

@@ -52,7 +52,7 @@ buildscript {
     /**
      * APP版本号
      */
-    ext.app_version = "1.0"
+    ext.app_version = "1.0.1"
 
     /**
      * 项目依赖库
@@ -62,10 +62,9 @@ buildscript {
             /**
              * 依赖仓储
              */
-            //maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
-            //maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
-            //maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
-            //maven { url 'https://jitpack.io' }
+            maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+            maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+            maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
 
             jcenter()
             mavenCentral()
@@ -96,21 +95,32 @@ buildscript {
          */
         classpath "org.aspectj:aspectjtools:$aspectj_version"
     }
+
+    repositories {
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+
+        jcenter()
+        mavenCentral()
+        google()
+
+        maven { url "https://jitpack.io" }
+    }
 }
 /**
  * 统一依赖仓储
  */
 allprojects {
     repositories {
-        //maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
-        //maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
-        //maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
-        //maven { url 'https://jitpack.io' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
 
-        google()
         jcenter()
         mavenCentral()
-        maven { url 'https://dl.bintray.com/geamtear/maven' }
+        google()
+
         maven { url "https://jitpack.io" }
     }
     tasks.withType(Javadoc) { // 新增

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

@@ -14,7 +14,8 @@
         android:supportsRtl="true" >
 
         <activity android:name="com.wdkl.app.ncs.callingdoor.activity.CallingdoorActivity"
-            android:screenOrientation="landscape"/>
+            android:screenOrientation="landscape"
+            android:launchMode="singleInstance"/>
 
         <activity android:name="com.wdkl.app.ncs.callingdoor.activity.AppUpdateActivity"
             android:screenOrientation="landscape"

+ 73 - 165
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/activity/CallingdoorActivity.kt

@@ -2,40 +2,27 @@ package com.wdkl.app.ncs.callingdoor.activity
 
 import android.content.*
 import android.net.ConnectivityManager
-import android.os.CountDownTimer
+import android.os.Bundle
 import android.os.Handler
-import android.os.SystemClock
 import android.support.v4.app.Fragment
-import android.support.v7.widget.LinearLayoutManager
 import android.text.TextUtils
 import android.util.Log
 import android.view.View
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.google.gson.Gson
+import com.wdkl.app.ncs.callingbed2.fragment.SkyCallFragment
 import com.wdkl.app.ncs.callingdoor.BuildConfig
-//import com.wdkl.app.ncs.callingbed2.sip.SipHelper
-//import com.wdkl.app.ncs.callingbed2.sip.SipStatus
-//import com.vvsip.ansip.IVvsipServiceListener
-//import com.vvsip.ansip.VvsipCall
 import com.wdkl.app.ncs.callingdoor.R
-import com.wdkl.app.ncs.callingdoor.adapter.NurseConfigAdpter
 import com.wdkl.app.ncs.callingdoor.agreement.CallingdoorAgreement
 import com.wdkl.app.ncs.callingdoor.databinding.CallingdoorMainLayBinding
-//import com.wdkl.app.ncs.callingbed2.common.Constant
-//import com.wdkl.app.ncs.callingbed2.common.MessageEvent
 import com.wdkl.app.ncs.callingdoor.fragment.*
 import com.wdkl.app.ncs.callingdoor.helper.*
 import com.wdkl.app.ncs.callingdoor.launch.CallingdoorLaunch
 import com.wdkl.app.ncs.callingdoor.settings.SettingConfig
-//import com.wdkl.app.ncs.sip.event.AEvent
-//import com.wdkl.app.ncs.sip.event.IEventListener
-//import com.wdkl.app.ncs.sip.helper.StarRtcHelper
-//import com.wdkl.app.ncs.sip.service.SipService
 import com.wdkl.core.consts.Urls
 import com.wdkl.core.socket.IUserState
 import com.wdkl.core.socket.SocketManager
-import com.wdkl.core.voip.CallSingleActivity
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.utils.*
 import com.wdkl.ncs.android.lib.vo.filter
@@ -46,11 +33,8 @@ import com.wdkl.ncs.android.middleware.common.SipStatus
 import com.wdkl.ncs.android.middleware.logic.contract.callingdoor.CallingdoorActivityContract
 import com.wdkl.ncs.android.middleware.logic.presenter.callingdoor.CallingdoorActivityPresenter
 import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
-import com.wdkl.ncs.android.middleware.model.dto.NurseConfigDto
 import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
 import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
-import com.wdkl.ncs.android.middleware.model.vo.BedDeviceInfoVO
-import com.wdkl.ncs.android.middleware.model.vo.CallRecordVO
 import com.wdkl.ncs.android.middleware.model.vo.DeviceRoomInfoVO
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
 import com.wdkl.ncs.android.middleware.tcp.TcpClient
@@ -75,7 +59,7 @@ import serialporttest.utils.SerialPortUtil
 
 @Router(path = "/callingdoor/main")
 class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, CallingdoorMainLayBinding>(), CallingdoorActivityContract.View, CallingdoorAgreement,
-        /*IEventListener,*/ SerialPortUtil.ISerialPortBedOnclickEvent, SerialPortUtil.ISerialPortBedOnclickString, IUserState {
+    SerialPortUtil.ISerialPortBedOnclickEvent, SerialPortUtil.ISerialPortBedOnclickString, IUserState {
 
     private lateinit var receiver: TimeReceiver
     private lateinit var curFragment: String
@@ -84,8 +68,8 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
     //二维码
     private val qrFragment = "qr_fragment"
 
-    //呼叫倒计时
-    lateinit var countDownTimer: CountDownTimer
+    //通话界面fragment
+    private var skyCallFragment: Fragment? = null
 
     private var updating :Boolean = false
 
@@ -108,14 +92,10 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         Constant.LOCAL_MAC = NetHelper.getInstance().macAddress
 
         presenter.loadTcpServerHost()
-        //显示日期时间
-        updateDateTime()
         //注册广播
         regReceiver()
         RecordHelper.getInstance().init()
 
-        initCountDownTimer()
-
         if (!EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().register(this)
         }
@@ -159,6 +139,24 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         curFragment = tag
     }
 
+    fun addCallFragment(fragment: Fragment) {
+        skyCallFragment = fragment
+        supportFragmentManager.beginTransaction()
+            .setCustomAnimations(R.anim.slide_down_in, R.anim.slide_up_out)
+            .add(R.id.call_frame, fragment)
+            .commit()
+    }
+
+    fun removeCallFragment() {
+        if (skyCallFragment != null) {
+            supportFragmentManager.beginTransaction()
+                //.setCustomAnimations(R.anim.slide_down_in, R.anim.slide_down_out)
+                .remove(skyCallFragment)
+                .commit()
+            skyCallFragment = null
+        }
+    }
+
     fun initDevice() {
         if (!TextUtils.isEmpty(Constant.LOCAL_MAC)) {
             presenter.loadDeviceInfo(Constant.LOCAL_MAC)
@@ -221,16 +219,13 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         }
 
         app_version.setOnLongClickListener {
-            AppInfoDialogHelper.showAppDialog(this@CallingdoorActivity, object :DialogInterface.OnClickListener {
-                override fun onClick(dialog: DialogInterface?, which: Int) {
-                    AppUpdateHelper.reboot(activity)
-                }
-            }, object :DialogInterface.OnClickListener {
-                override fun onClick(dialog: DialogInterface?, which: Int) {
+            AppInfoDialogHelper.showAppDialog(this@CallingdoorActivity,
+                { dialog, which -> AppUpdateHelper.reboot(activity) },
+                { dialog, which ->
                     Constant.silentUpdate = false
                     checkAppVersion()
-                }
-            })
+                })
+
             return@setOnLongClickListener true
         }
     }
@@ -243,23 +238,6 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         SerialPortUtil.getInstance().startHeartBeat()
     }
 
-    fun initCountDownTimer() {
-        val overTime = SettingConfig.getSipOverTime(this) * 1000L
-        countDownTimer = object: CountDownTimer(overTime, 1000) {
-            override fun onTick(millisUntilFinished: Long) {
-                //
-            }
-
-            override fun onFinish() {
-                //呼叫超时,返回到主界面
-                showMessage("无人应答...")
-                Constant.CALL_STATE = Constant.CALL_STANDBY
-                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
-                CallDialogHelper.dismissCallDialog()
-            }
-        }
-    }
-
     override fun checkAppVersion() {
         Constant.APP_PATH = ""
         //获取APP版本信息,门口机type=3
@@ -271,7 +249,6 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
 
 
     override fun destory() {
-        countDownTimer.cancel()
         unRegReceiver()
         if (EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().unregister(this)
@@ -345,8 +322,8 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         }
 
         //检查版本
-        Constant.silentUpdate = true
-        checkAppVersion()
+        //Constant.silentUpdate = true
+        //checkAppVersion()
     }
 
     private fun connectSocket(){
@@ -366,11 +343,11 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         SettingConfig.setDaytimeBrightness(this, partSetting.dayLight)
         SettingConfig.setNightBrightness(this, partSetting.nightLight)
 
-        //分机白天夜晚系统音量
+        //白天夜晚系统音量
         SettingConfig.setDoorDaytimeSystemVolume(this, partSetting.dayDoorVol)
         SettingConfig.setDoorNightSystemVolume(this, partSetting.nightDoorVol)
 
-        //分机通话音量  没做白昼区分
+        //通话音量  没做白昼区分
         SettingConfig.setExtensionCallVolume(this, partSetting.dayDoorVol)
 
         SettingConfig.setSipOverTime(this, partSetting.sipOvertime)
@@ -440,13 +417,13 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
                     } else {
                         showMessage("当前设备可能未注册,不能呼叫")
                     }
-                } else if (Constant.CALL_STATE == Constant.CALL_INCOMING) {
+                } /*else if (Constant.CALL_STATE == Constant.CALL_INCOMING) {
                     inCalling()
                 } else if (Constant.CALL_STATE == Constant.CALL_CALLING) {
                     hangupCall()
                 } else if (Constant.CALL_STATE == Constant.CALL_OUTGOING) {
                     cancelCall()
-                }
+                }*/
             }
         }
     }
@@ -478,39 +455,19 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
 
     //开始呼叫
     fun startCall(type: Int) {
-        VoiceUtil.startAudioCall(Constant.DEVICE_ID)
-        Constant.CALL_TYPE = type
-        Constant.CALL_STATE = Constant.CALL_OUTGOING
-    }
-
-    //取消呼叫
-    fun cancelCall() {
-        VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
-        CallDialogHelper.dismissCallDialog()
-        Constant.CALL_STATE = Constant.CALL_STANDBY
-        countDownTimer.cancel()
-    }
-
-    //进入通话
-    fun inCalling() {
-        VoiceUtil.acceptAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
-        CallDialogHelper.dismissCallDialog()
-        Constant.CALL_STATE = Constant.CALL_CALLING
-        countDownTimer.cancel()
-    }
-
-    //挂断通话
-    fun hangupCall() {
-        VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
-        SocketManager.getInstance().endCall()
-        Constant.CALL_STATE = Constant.CALL_STANDBY
-    }
-
-    //来电拒接
-    fun rejectCall() {
-        VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
-        CallDialogHelper.dismissCallDialog()
-        Constant.CALL_STATE = Constant.CALL_STANDBY
+        //通话之前先判断webrtc socket和tcp是否连接正常,否则不能建立通话
+        if (SocketManager.getInstance().socketOpen() && Constant.TCP_CONNECTED) {
+            //去电界面
+            Constant.CALL_TYPE = type
+            Constant.CALL_STATE = Constant.CALL_OUTGOING
+            var fragment = SkyCallFragment()
+            var bundle = Bundle()
+            bundle.putInt("call_state", 0)
+            fragment.arguments = bundle
+            addCallFragment(fragment)
+        } else {
+            showMessage("通话服务或网络未连接,请检查网络稍后再试")
+        }
     }
 
     @Subscribe(threadMode = ThreadMode.MAIN)
@@ -525,82 +482,39 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
                 }
             }
 
+            //退出通话界面
+            Constant.EVENT_REMOVE_CALL_FRAGMENT -> {
+                if (skyCallFragment != null) {
+                    Constant.CALL_STATE = Constant.CALL_STANDBY
+                    removeCallFragment()
+                }
+            }
+
             //TCP消息处理
             Constant.EVENT_TCP_MSG -> {
                 if (messageEvent.message is TcpModel) {
                     val tcpModel = messageEvent.message as TcpModel
-                    val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                     Log.d("TCP", "received tcp action: " + tcpModel.action + ", type: " + tcpModel.type)
                     if (tcpModel.getType() == TcpType.VOICE) {
+                        val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
                         if (tcpModel.action == TcpAction.VoiceAction.CALL) {
-                            //当前为待机状态,切换到来电界面,否则不做处理
-                            if (Constant.CALL_STATE == Constant.CALL_STANDBY) {
-                                Constant.fromId = tcpModel.fromId
-                                Constant.interactionId = interactionVO.id
-                                Constant.CALL_STATE = Constant.CALL_INCOMING
-
-                                CallDialogHelper.dismissCallDialog()
-                                CallDialogHelper.showCallDialog(this@CallingdoorActivity, 1, View.OnClickListener {
-                                    //呼出取消
-                                }, View.OnClickListener {
-                                    //来电接听
-                                    inCalling()
-                                }, View.OnClickListener {
-                                    //来电拒接
-                                    rejectCall()
-                                })
-                            }
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
-                            //我方呼出,对方接受
-                            Constant.interactionId = interactionVO.id
                             Constant.fromId = tcpModel.fromId
-                            CallDialogHelper.dismissCallDialog()
-                            countDownTimer.cancel()
-                            Constant.CALL_STATE = Constant.CALL_CALLING
-
-                            if (Constant.CALL_TYPE == Constant.VIDEO_CALL) {
-                                CallSingleActivity.openActivity(this, interactionVO.toSipId, true, interactionVO.toSipId, false, false)
+                            Constant.interactionId = interactionVO.id
+                            Constant.CALL_STATE = Constant.CALL_INCOMING
+                            //通话之前先判断webrtc socket是否连接上,否则不能建立通话
+                            if (SocketManager.getInstance().socketOpen()) {
+                                //来电界面
+                                var fragment = SkyCallFragment()
+                                var bundle = Bundle()
+                                bundle.putInt("call_state", 1)
+                                bundle.putSerializable("tcp_model", tcpModel)
+                                fragment.arguments = bundle
+                                addCallFragment(fragment)
                             } else {
-                                CallSingleActivity.openActivity(this, interactionVO.toSipId, true, interactionVO.toSipId, true, false)
+                                showMessage("通话服务还未建立连接,请稍后")
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
                             }
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
-                            //我方呼出,对方拒绝
-                            showMessage("对方已拒绝!")
-                            Constant.CALL_STATE = Constant.CALL_STANDBY
-                            CallDialogHelper.dismissCallDialog()
-                            countDownTimer.cancel()
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
-                            //我方呼出,对方通话中
-                            showMessage("对方正在忙线中,暂时无法接听!")
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
-                            //呼叫成功
-                            Constant.interactionId = interactionVO.id
-                            countDownTimer.start()
-                            Constant.CALL_STATE = Constant.CALL_OUTGOING
-
-                            CallDialogHelper.dismissCallDialog()
-                            CallDialogHelper.showCallDialog(this@CallingdoorActivity, 0, View.OnClickListener {
-                                //呼出取消
-                                cancelCall()
-                            }, View.OnClickListener {
-                                //来电接听
-                            }, View.OnClickListener {
-                                //来电拒接
-                            })
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
-                            //我方呼出,对方不在线,设备离线或其它错误
-                            showMessage("呼叫失败,对方可能不在线!")
-                            Constant.CALL_STATE = Constant.CALL_STANDBY
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) {
-                            //对方挂断,不论我方呼出或呼入
-                            Constant.CALL_STATE = Constant.CALL_STANDBY
-                            CallDialogHelper.dismissCallDialog()
-                            countDownTimer.cancel()
-                        } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
-                            //对方呼叫时取消
-                            Constant.CALL_STATE = Constant.CALL_STANDBY
-                            CallDialogHelper.dismissCallDialog()
-                            countDownTimer.cancel()
                         }
                     } else if (tcpModel.type == TcpType.SOS) {
                         if (tcpModel.action == TcpAction.SOSAction.CANCEL) {
@@ -614,7 +528,7 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
                             checkAppVersion()
                         }
                     } else if (tcpModel.action == TcpAction.DataAction.REFRESH) {
-                        //重新加载数据,比如出院,入院等
+                        //重新加载数据
                         initDevice()
                     }
                 }
@@ -643,10 +557,6 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         }
     }
 
-    private fun updateDateTime() {
-        view_title_layout_tv_time.setText(TimeHandle.getDateTime("yyyy-MM-dd HH:mm E"));
-    }
-
     private fun updateNetState() {
         if (NetHelper.getInstance().networkType == ConnectivityManager.TYPE_WIFI) {
             view_title_layout_iv_wifi.visibility = View.VISIBLE
@@ -662,13 +572,12 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
             view_title_layout_iv_wifi.visibility = View.GONE
         }
 
-        if (NetHelper.isBTConnected()) {
+        /*if (NetHelper.isBTConnected()) {
             view_title_layout_iv_bt.visibility = View.VISIBLE
             view_title_layout_iv_bt.setImageResource(R.mipmap.ic_bt_success)
         } else {
             view_title_layout_iv_bt.visibility = View.GONE
-        }
-        view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_daylight)
+        }*/
 
         /*
         * 检查网络情况,若tcp断开连接多次且IP也是空的则网络异常,重启设备
@@ -744,7 +653,6 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
             if (intent.action == Intent.ACTION_TIME_TICK
                     || intent.action == Intent.ACTION_TIME_CHANGED
                     || intent.action == Intent.ACTION_TIMEZONE_CHANGED) {
-                updateDateTime()
                 updateNetState()
                 updateSettings()
 

+ 145 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/BaseCallFragment.kt

@@ -0,0 +1,145 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.Bundle
+import android.os.CountDownTimer
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.enation.javashop.utils.base.tool.BaseToolActivity
+import com.wdkl.app.ncs.callingdoor.helper.RingPlayHelper
+import com.wdkl.app.ncs.callingdoor.settings.SettingConfig
+import com.wdkl.core.voip.VoipEvent
+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.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.skywebrtc.SkyEngineKit
+import com.wdkl.skywebrtc.except.NotInitializedException
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+abstract class BaseCallFragment: Fragment() {
+
+    private var layout: View? = null
+
+    protected lateinit var baseActivity: BaseToolActivity
+
+    //通话状态:0-去电, 1-来电, 2-探视
+    protected var callState : Int = 0
+    protected var tcpModel: TcpModel? = null
+
+    //计时器
+    lateinit var countDownTimer: CountDownTimer
+
+    protected var gEngineKit: SkyEngineKit? = null
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        retainInstance = true
+        callState = arguments.getInt("call_state")
+        if (arguments.getSerializable("tcp_model") != null) {
+            tcpModel = arguments.getSerializable("tcp_model") as TcpModel
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        if (layout == null) {
+            layout = inflater.inflate(getLayId(), null)
+        }
+
+        /**初始化宿主Activity*/
+        baseActivity = getActivity() as BaseToolActivity
+
+        return layout
+    }
+
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        try {
+            SkyEngineKit.init(VoipEvent())
+            gEngineKit = SkyEngineKit.Instance()
+        } catch (e: NotInitializedException) {
+            SkyEngineKit.init(VoipEvent())
+            try {
+                gEngineKit = SkyEngineKit.Instance()
+            } catch (ex: NotInitializedException) {
+                ex.printStackTrace()
+                baseActivity.finish()
+            }
+        }
+
+        init()
+        bindEvent()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        destroy()
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    protected abstract fun getLayId(): Int
+
+    protected abstract fun init()
+
+    protected abstract fun bindEvent()
+
+    protected abstract fun destroy()
+
+    //初始化计时器
+    protected fun initCountDownTimer(view: TextView) {
+        val overTime = SettingConfig.getSipOverTime(baseActivity) * 1000L
+        countDownTimer = object: CountDownTimer(overTime, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+                if (view != null) {
+                    val time = millisUntilFinished/1000
+                    view.setText("倒计时: " + time + "秒")
+                }
+            }
+
+            override fun onFinish() {
+                //呼叫超时,返回到主界面
+                RingPlayHelper.stopRingTone()
+                showMessage("无人应答...")
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                backToMain()
+            }
+        }
+    }
+
+    //开始计时
+    protected fun startTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.start()
+        }
+    }
+
+    //取消计时器
+    protected fun cancelTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.cancel()
+        }
+    }
+
+    //返回主界面
+    protected fun backToMain() {
+        EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+    }
+
+}

+ 2 - 2
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/MainFragment.kt

@@ -63,9 +63,9 @@ class MainFragment: BaseFragment<MainFragmentPresenter, MainViewLayoutBinding>()
     }
 
     override fun showRoomInfo(roomInfo: FrameRoomVO) {
-        if (roomInfo.frameBedVos != null) {
+        if (roomInfo.frameBedList != null) {
             bedList.clear()
-            bedList.addAll(roomInfo.frameBedVos)
+            bedList.addAll(roomInfo.frameBedList)
             roomAdpter.setData(bedList)
             rv_bed_view.itemAnimator = DefaultItemAnimator()
             if (bedList.size > 3) {

+ 452 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/SkyCallFragment.kt

@@ -0,0 +1,452 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.google.gson.Gson
+import com.wdkl.app.ncs.callingdoor.R
+import com.wdkl.app.ncs.callingdoor.helper.RingPlayHelper
+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.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.skywebrtc.CallSession
+import com.wdkl.skywebrtc.EnumType
+import kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import org.webrtc.SurfaceViewRenderer
+import java.util.*
+
+class SkyCallFragment: BaseCallFragment(), CallSession.CallSessionCallback {
+
+    //来电设备id
+    var fromId: Int = -1
+
+    private var interactionVO: InteractionVO? = null
+
+    private var localSurfaceView: SurfaceViewRenderer? = null
+    private var remoteSurfaceView: SurfaceViewRenderer? = null
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    private var audioCall: Boolean = false
+
+    override fun getLayId(): Int {
+        return R.layout.sky_voice_call_layout
+    }
+
+    override fun init() {
+        //初始化计时器
+        initCountDownTimer(sky_voice_call_timeout)
+        //tcp参数
+        if (tcpModel != null) {
+            fromId = tcpModel!!.fromId
+            interactionVO = Gson().fromJson(tcpModel!!.data.toString(), InteractionVO::class.java)
+        }
+
+        when (callState) {
+            0 -> {
+                //去电
+                startOutgoing()
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_back2, true)
+            }
+
+            1 -> {
+                //来电
+                showIncomingCall()
+                RingPlayHelper.playRingTone(baseActivity, R.raw.incoming_call, true)
+            }
+        }
+    }
+
+    override fun bindEvent() {
+        //去电取消或通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                //结束sip通话
+                val session = gEngineKit?.getCurrentSession()
+                if (session != null) {
+                    session.leave()
+                }
+
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                sky_voice_call_timer.stop()
+                backToMain()
+            } else {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                cancelCall()
+            }
+        }
+
+        //来电拒绝
+        sky_voice_call_ring_reject.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+            backToMain()
+        }
+
+        //来电接听
+        sky_voice_call_ring_pickup_audio.setOnClickListener {
+            acceptCall()
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_INCOMING
+            VoiceUtil.acceptAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+        }
+    }
+
+    override fun destroy() {
+        cancelTimer()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        RingPlayHelper.stopRingTone()
+    }
+
+    private fun startOutgoing() {
+        VoiceUtil.startAudioCall(Constant.DEVICE_ID)
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        sky_voice_call_timeout.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+        startTimer()
+    }
+
+    //去电界面
+    private fun showOutgoingCall() {
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        sky_voice_call_calling_text.text = "呼叫成功,等待接听..."
+        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()
+    }
+
+    //来电界面
+    private fun showIncomingCall() {
+        Constant.CALL_STATE = Constant.CALL_INCOMING
+        sky_voice_call_calling_text.text = "有新来电..."
+        sky_voice_call_outgoing.visibility = View.GONE
+        sky_voice_call_incoming.visibility = View.VISIBLE
+        sky_voice_call_timeout.visibility = View.GONE
+        sky_voice_call_timer.visibility = View.GONE
+        cancelTimer()
+    }
+
+    //开始接听
+    private fun acceptCall() {
+        sky_voice_call_calling_text.text = "连接中..."
+        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
+        sky_voice_call_timer.stop()
+        backToMain()
+    }
+
+    //语音接通
+    private fun joinAudioCall() {
+        val session = gEngineKit?.getCurrentSession()
+        if (session != null) {
+            Log.e("dds", "audio call session state: " + session.state)
+            session.setSessionCallback(this)
+        }
+        if (session != null && session.state == EnumType.CallState.Incoming) {
+            session.joinHome(session.roomId)
+            session.toggleSpeaker(true)
+        }
+    }
+
+    //视频接通
+    private fun joinVideoCall() {
+        val session = gEngineKit?.getCurrentSession()
+        if (session != null) {
+            Log.e("dds", "video call session state: " + session.state)
+            session.setSessionCallback(this)
+        }
+        if (session != null && session.state == EnumType.CallState.Incoming) {
+            val surfaceView = gEngineKit!!.currentSession.setupLocalVideo(false)
+            if (surfaceView != null) {
+                localSurfaceView = surfaceView as SurfaceViewRenderer
+                localSurfaceView!!.setZOrderMediaOverlay(false)
+                fullscreen_video_frame.addView(localSurfaceView)
+            }
+
+            session.joinHome(session.roomId)
+            session.toggleSpeaker(true)
+        }
+    }
+
+    private fun showCalling(audioOnly: Boolean) {
+        if (audioOnly) {
+            //移除视频画面
+            fullscreen_video_frame.visibility = View.GONE
+            pip_video_frame.visibility = View.GONE
+            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.text = "通话中..."
+        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()
+    }
+
+    //创建会话
+    private fun startCall(targetId: String, audioOnly: Boolean): Boolean {
+        audioCall = audioOnly
+        val room = UUID.randomUUID().toString() + System.currentTimeMillis()
+        val b = gEngineKit!!.startOutCall(baseActivity, room, targetId, audioOnly)
+        if (b) {
+            val session = gEngineKit!!.currentSession
+            if (session == null) {
+                return false
+            } else {
+                session.setSessionCallback(this)
+                session.toggleSpeaker(true)
+            }
+        }
+        return b
+    }
+
+    //通话结束
+    private fun callEnd() {
+        Log.e("dds", "call end !!!!!!!!!!!!!!!!!!")
+
+        VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        if (gEngineKit != null && gEngineKit!!.currentSession != null && gEngineKit!!.currentSession.state != EnumType.CallState.Idle) {
+            gEngineKit!!.endCall()
+        }
+        backToMain()
+    }
+
+
+    /********************************************************
+     ********************* webrtc通话回调 ********************
+     * 注意: 如涉及到UI更新的需要在主线程处理,务必注意
+     *******************************************************/
+    override fun didChangeState(state: EnumType.CallState?) {
+        Log.e("dds", "didChangeState: " + state)
+        handler.post {
+            if (state == EnumType.CallState.Connected) {
+                //更新界面显示
+                showCalling(audioCall)
+            }
+        }
+    }
+
+    override fun didDisconnected(userId: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+    override fun didError(error: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+    //处理本地视频画面
+    override fun didCreateLocalVideoTrack() {
+        Log.e("dds", "didCreateLocalVideoTrack")
+        handler.post {
+            if (localSurfaceView == null) {
+                val surfaceView = gEngineKit!!.currentSession.setupLocalVideo(true)
+                Log.e("dds", "didCreateLocalVideoTrack surfaceView: " + surfaceView)
+                if (surfaceView != null) {
+                    localSurfaceView = surfaceView as SurfaceViewRenderer
+                }
+            }
+        }
+    }
+
+    //处理远端视频画面
+    override fun didReceiveRemoteVideoTrack(userId: String?) {
+        Log.e("dds", "didReceiveRemoteVideoTrack  userId: " + userId)
+        handler.post {
+            //本地画面
+            if (localSurfaceView != null) {
+                localSurfaceView!!.setZOrderMediaOverlay(true)
+                if (localSurfaceView!!.parent != null) {
+                    (localSurfaceView!!.parent as ViewGroup).removeView(localSurfaceView)
+                }
+                pip_video_frame!!.addView(localSurfaceView)
+            }
+
+            //远端画面
+            val surfaceView = gEngineKit!!.currentSession.setupRemoteVideo(userId, false)
+            Log.e("dds", "didReceiveRemoteVideoTrack,surfaceView = $surfaceView")
+            if (surfaceView != null) {
+                remoteSurfaceView = surfaceView as SurfaceViewRenderer
+                fullscreen_video_frame.removeAllViews()
+                if (remoteSurfaceView!!.parent != null) {
+                    (remoteSurfaceView!!.parent as ViewGroup).removeView(remoteSurfaceView)
+                }
+                fullscreen_video_frame.addView(remoteSurfaceView)
+            }
+        }
+    }
+
+    override fun didCallEndWithReason(callEndReason: EnumType.CallEndReason?) {
+        handler.post {
+            when (callEndReason) {
+                EnumType.CallEndReason.Busy -> {
+                    showMessage("对方忙线中")
+                }
+                EnumType.CallEndReason.AcceptByOtherClient -> {
+                    showMessage("通话中")
+                }
+                EnumType.CallEndReason.Hangup -> {
+                    //showMessage("挂断")
+                }
+                EnumType.CallEndReason.MediaError -> {
+                    showMessage("媒体错误")
+                }
+                EnumType.CallEndReason.OpenCameraFailure -> {
+                    showMessage("打开摄像头错误")
+                }
+                EnumType.CallEndReason.RemoteHangup -> {
+                    showMessage("对方挂断")
+                }
+                EnumType.CallEndReason.RemoteSignalError -> {
+                    showMessage("对方网络断开")
+                }
+                EnumType.CallEndReason.SignalError -> {
+                    showMessage("连接断开")
+                }
+                EnumType.CallEndReason.Timeout -> {
+                    showMessage("对方未接听")
+                }
+            }
+
+            callEnd()
+        }
+    }
+
+    override fun didChangeMode(isAudioOnly: Boolean) {
+        handler.post {
+            //
+        }
+    }
+
+    override fun didUserLeave(userId: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.type) {
+            Constant.EVENT_SIP_CALL_STATUS -> {
+                //收到sip通话邀请,加入通话
+                Log.e("dds", "EVENT_SIP_CALL_STATUS: " + messageEvent.message)
+                if (messageEvent.message is String) {
+                    val call = messageEvent.message as String
+                    if (call.equals("video_call")) {
+                        audioCall = false
+                        Handler().postDelayed({
+                            joinVideoCall()
+                        }, 2000)
+                    } else {
+                        audioCall = true
+                        Handler().postDelayed({
+                            joinAudioCall()
+                        }, 1500)
+                    }
+                }
+            }
+
+            Constant.EVENT_TCP_MSG -> {
+                if (messageEvent.message is TcpModel) {
+                    val curTcpModel = messageEvent.message as TcpModel
+                    if (curTcpModel.getType() == TcpType.VOICE) {
+                        val curInteractionVO = Gson().fromJson(curTcpModel.data.toString(), InteractionVO::class.java)
+                        if (curTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                            //我方呼出,对方接受
+                            RingPlayHelper.stopRingTone()
+                            Constant.interactionId = curInteractionVO.id
+                            fromId = curTcpModel.fromId
+                            acceptCall()
+                            if (!startCall(curInteractionVO.toSipId, Constant.CALL_TYPE == Constant.VOICE_CALL)) {
+                                //通话失败,重置并返回主界面
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, Constant.interactionId)
+                                if (sky_voice_call_timer != null) {
+                                    sky_voice_call_timer.stop()
+                                }
+                                backToMain()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
+                            //我方呼出,对方拒绝
+                            showMessage("对方已拒绝!")
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
+                            //我方呼出,对方通话中
+                            showMessage("对方正在忙线中,暂时无法接听!")
+                            AppTool.Time.delay(2000) {
+                                RingPlayHelper.stopRingTone()
+                                cancelCall()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
+                            //呼叫成功
+                            //本机呼叫的时候tcpmodel为空,只有呼叫成功的时候才能获得对应tcp相关数据
+                            interactionVO = curInteractionVO
+                            Constant.interactionId = curInteractionVO.id
+                            showOutgoingCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                            //我方呼出,对方不在线,设备离线或其它错误
+                            showMessage("呼叫失败,找不到设备或对方不在线!")
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) {
+                            //对方挂断,不论我方呼出或呼入
+                            if (Constant.interactionId == curInteractionVO.id) {
+                                RingPlayHelper.stopRingTone()
+                                cancelCall()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+                            //对方呼叫时取消
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 24 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/helper/RingPlayHelper.java

@@ -0,0 +1,24 @@
+package com.wdkl.app.ncs.callingdoor.helper;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+import com.wdkl.core.voip.AsyncPlayer;
+
+public class RingPlayHelper {
+
+    private static AsyncPlayer ringPlayer;
+
+    public static void playRingTone(Context context, int res, boolean loop) {
+        if (ringPlayer == null) {
+            ringPlayer = new AsyncPlayer(null);
+        }
+        ringPlayer.play(context, res, loop, AudioManager.STREAM_MUSIC);
+    }
+
+    public static void stopRingTone() {
+        if (ringPlayer != null) {
+            ringPlayer.stop();
+        }
+    }
+}

+ 7 - 0
callingdoor/src/main/res/anim/slide_down_in.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromYDelta="100%p"
+        android:toYDelta="0%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingdoor/src/main/res/anim/slide_left_in.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromXDelta="-100%p"
+        android:toXDelta="0%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingdoor/src/main/res/anim/slide_right_out.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromXDelta="0%p"
+        android:toXDelta="100%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingdoor/src/main/res/anim/slide_up_out.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromYDelta="0%p"
+        android:toYDelta="-100%p"
+        android:duration="300"/>
+</set>

+ 6 - 1
callingdoor/src/main/res/layout/callingdoor_main_lay.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:bind="http://schemas.android.com/apk/res-auto">
+<layout>
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -102,5 +102,10 @@
             android:layout_below="@id/tv_room_name"
             android:layout_toLeftOf="@id/rl_room_actions"/>
 
+        <!--通话界面-->
+        <FrameLayout
+            android:id="@+id/call_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
     </RelativeLayout>
 </layout>

+ 134 - 0
callingdoor/src/main/res/layout/sky_voice_call_layout.xml

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/background_dark">
+        <!--全屏视频画面-->
+        <FrameLayout
+            android:id="@+id/fullscreen_video_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+
+        <!--小窗视频画面-->
+        <FrameLayout
+            android:id="@+id/pip_video_frame"
+            android:layout_width="200dp"
+            android:layout_height="240dp"
+            android:layout_gravity="top|end"
+            android:layout_marginHorizontal="10dp"
+            android:layout_marginTop="10dp" />
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <!--语音呼叫layout-->
+            <LinearLayout
+                android:id="@+id/ll_voice_call"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/sky_voice_call_calling_text"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="48dp"
+                    android:gravity="center"
+                    android:text="正在呼叫..."
+                    android:textColor="#9E9E9F"
+                    android:textSize="32sp" />
+
+                <ImageView
+                    android:id="@+id/sky_voice_call_head_img"
+                    android:layout_width="120dp"
+                    android:layout_height="120dp"
+                    android:layout_marginTop="24dp"
+                    android:scaleType="centerInside"
+                    android:src="@drawable/ic_nurse" />
+
+                <TextView
+                    android:id="@+id/sky_voice_call_timeout"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dp"
+                    android:gravity="center"
+                    android:text="倒计时: 00"
+                    android:textColor="#9E9E9F"
+                    android:textSize="24sp" />
+            </LinearLayout>
+
+            <!--呼出-->
+            <LinearLayout
+                android:id="@+id/sky_voice_call_outgoing"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_centerHorizontal="true"
+                android:layout_marginBottom="80dp"
+                android:gravity="center_horizontal"
+                android:orientation="vertical"
+                android:visibility="visible">
+
+                <Chronometer
+                    android:id="@+id/sky_voice_call_timer"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    android:text="00:00"
+                    android:textColor="@color/white"
+                    android:textSize="24sp" />
+
+                <ImageView
+                    android:id="@+id/sky_voice_call_hangup"
+                    android:layout_width="100dp"
+                    android:layout_height="100dp"
+                    android:layout_marginTop="20dp"
+                    android:src="@drawable/selector_call_hangup" />
+            </LinearLayout>
+
+            <!--来电-->
+            <LinearLayout
+                android:id="@+id/sky_voice_call_incoming"
+                android:layout_width="match_parent"
+                android:layout_height="140dp"
+                android:layout_alignParentBottom="true"
+                android:layout_centerHorizontal="true"
+                android:layout_marginBottom="80dp"
+                android:gravity="bottom"
+                android:orientation="horizontal"
+                android:visibility="gone">
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="2" />
+
+                <ImageView
+                    android:id="@+id/sky_voice_call_ring_reject"
+                    android:layout_width="100dp"
+                    android:layout_height="100dp"
+                    android:src="@drawable/selector_call_hangup" />
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="1" />
+
+                <ImageView
+                    android:id="@+id/sky_voice_call_ring_pickup_audio"
+                    android:layout_width="100dp"
+                    android:layout_height="100dp"
+                    android:src="@drawable/selector_call_answer" />
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="1dp"
+                    android:layout_weight="2" />
+            </LinearLayout>
+        </RelativeLayout>
+    </FrameLayout>
+</layout>

+ 4 - 4
callingdoor/src/main/res/layout/view_title_layout.xml

@@ -27,14 +27,14 @@
         android:textSize="@dimen/font_size_20" />
 
     <!--日期时间-->
-    <TextView
-        android:id="@+id/view_title_layout_tv_time"
+    <TextClock
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerHorizontal="true"
         android:layout_centerVertical="true"
-        android:layout_marginRight="5dp"
-        android:text="--"
+        android:format12Hour="yyyy-MM-dd HH:mm:ss EEEE"
+        android:format24Hour="yyyy-MM-dd HH:mm:ss EEEE"
+        android:timeZone="GMT+8"
         android:textColor="@color/main_color"
         android:textSize="@dimen/font_size_20" />
 

二進制
callingdoor/src/main/res/raw/ring_back2.wav


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

@@ -109,7 +109,7 @@ public class Constant {
     //设备是否有camera
     public static boolean supportCamera = false;
 
-    //是否手动升级
+    //是否静默升级
     public static boolean silentUpdate = true;
 
     /**
@@ -151,4 +151,9 @@ public class Constant {
      * 通话状态
      */
     public static final int EVENT_CALL_STATE = 0x08;
+
+    /**
+     * 退出通话界面
+     */
+    public static final int EVENT_REMOVE_CALL_FRAGMENT = 0x09;
 }

+ 5 - 5
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/FrameRoomVO.java

@@ -16,7 +16,7 @@ public class FrameRoomVO implements Serializable {
 
     private FrameDO frameRoom;
     private Integer deviceCount;
-    private List<FrameBedVO> frameBedVos;
+    private List<FrameBedVO> frameBedList;
 
 
     public FrameDO getFrameRoom() {
@@ -35,11 +35,11 @@ public class FrameRoomVO implements Serializable {
         this.deviceCount = deviceCount;
     }
 
-    public List<FrameBedVO> getFrameBedVos() {
-        return frameBedVos;
+    public List<FrameBedVO> getFrameBedList() {
+        return frameBedList;
     }
 
-    public void setFrameBedVos(List<FrameBedVO> frameBedVos) {
-        this.frameBedVos = frameBedVos;
+    public void setFrameBedList(List<FrameBedVO> frameBedList) {
+        this.frameBedList = frameBedList;
     }
 }

+ 34 - 13
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClient.java

@@ -26,7 +26,9 @@ import io.netty.util.CharsetUtil;
 
 //单例
 public class TcpClient {
-    private NioEventLoopGroup workGroup = new NioEventLoopGroup(2);
+   private String TAG = TcpClient.class.getSimpleName();
+
+    private NioEventLoopGroup workGroup;
     public Channel channel;
     private Bootstrap bootstrap;
 
@@ -52,8 +54,10 @@ public class TcpClient {
     }
 
     //初始化Netty Tcp Client 并连接
-    public void init(String serverIP, Integer serverPort, Integer heartBeatSeconds){
+    public void init(String serverIP, Integer serverPort, Integer heartBeatSeconds) {
+
         final Integer hbSeconds = heartBeatSeconds;
+        workGroup = new NioEventLoopGroup(2);
         bootstrap = new Bootstrap();
         bootstrap.group(workGroup)
                 .channel(NioSocketChannel.class)
@@ -68,7 +72,8 @@ public class TcpClient {
                         // LengthFieldPrepender是一个编码器,主要是在响应字节数据前面添加字节长度字段
                         socketChannel.pipeline().addLast(new LengthFieldPrepender(2));
                         //心跳包应当小于服务器间隔
-                        socketChannel.pipeline().addLast(new IdleStateHandler(0, 0,hbSeconds, TimeUnit.SECONDS));
+//                        socketChannel.pipeline().addLast(new IdleStateHandler(0, hbSeconds, 0, TimeUnit.SECONDS));
+                        socketChannel.pipeline().addLast(new IdleStateHandler(hbSeconds*2, hbSeconds, 0, TimeUnit.SECONDS));
                         socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                         socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
                         socketChannel.pipeline().addLast(tcpClientHandler);
@@ -97,7 +102,8 @@ public class TcpClient {
             public void operationComplete(ChannelFuture channelFuture) throws Exception {
                 if (channelFuture.isSuccess()){
                     isRunning = true;
-                    retryTimes = 0;
+                    channel = channelFuture.channel();
+                    retryTimes = 1;
                     System.out.println("connect success");
                 } else {
                     //连接失败时的处理
@@ -108,17 +114,27 @@ public class TcpClient {
                         @Override
                         public void run() {
                             retryTimes++;
-                            if (retryTimes>30){
-                                System.out.println("重试"+(retryTimes-1)+"次,结束");
-                                workGroup.shutdownGracefully();
-                                inited = false;
-                                //从API获取新的serverIP和serverPort,全新连接
-                                TcpClient.getInstance().init(Constant.TCP_SERVER_URL, Constant.TCP_PORT, Constant.TCP_HEART_BEAT);
-                                return;
+                            if (retryTimes > 20) { //重试30次还没连成功,等10分钟后再试
+//                                System.out.println("TcpClient 重试" + (retryTimes - 1) + "次,结束");
+//                                channel.close();
+//                                workGroup.shutdownGracefully();
+//                                //todo: 从API获取新的serverIP和serverPort,全新连接
+//                                TcpClient.getInstance().init(Constant.TCP_SERVER_URL, Constant.TCP_PORT, Constant.TCP_HEART_BEAT);
+                                retryTimes=1;
+                                channelFuture.channel().eventLoop().schedule(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次");
+                                        doConnect();
+                                    }
+                                }, 60*10, TimeUnit.SECONDS);
+
+                            }else{
+                                doConnect();
                             }
-                            doConnect();
+
                         }
-                    },retrySeconds * retryTimes, TimeUnit.SECONDS);
+                    }, 5, TimeUnit.SECONDS);
                 }
             }
         });
@@ -144,6 +160,11 @@ public class TcpClient {
         }
     }
 
+
+    public Channel getChannel() {
+        return channel;
+    }
+
     //测试
     public static void main(String[] args) {
 

+ 32 - 27
middleware/src/main/code/com/wdkl/ncs/android/middleware/tcp/TcpClientHandler.java

@@ -82,18 +82,18 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
     //写心跳包。没有消息发送时,每间隔一定时间会由此方法向服务端发送心跳
     @Override
     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-        if (evt instanceof IdleStateEvent){
-            IdleStateEvent event = (IdleStateEvent)evt;
-            if (event.state()== IdleState.WRITER_IDLE){
-                //读心跳包超时执行
-                Log.i(TAG,"TcpClientHandler ===> WRITER_IDLE");
-            } else if (event.state() == IdleState.READER_IDLE){
-                //写心跳包超时
-                Log.i(TAG,"TcpClientHandler ===> READER_IDLE");
-            } else if (event.state() == IdleState.ALL_IDLE){
-                //写心跳包超时
-                Log.i(TAG,"TcpClientHandler ===> ALL_IDLE");
-                ctx.writeAndFlush("0");
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            if (event.state() == IdleState.WRITER_IDLE) { //超时未执行写操作,在指定的超时时间内未有写操作,要发送心跳包,告诉服务器连接还存活。服务器收到心跳立马回应,正常客户端收到后执行读操作,
+                ctx.writeAndFlush("0");                //这种情况下不会引发READER_IDLE事件。如果服务器因为网络或其他原因导致回应的心跳,客户端没有收到,在超过写超时时间2个周期后依然没有收到,
+            } else if (event.state() == IdleState.READER_IDLE) { //认为服务不可用,主动断开连接
+//                ctx.channel().close();
+                Log.i(TAG, "TcpClientHandler ===> READER_IDLE");
+                ctx.close();
+//                ctx.close();
+            } else if (event.state() == IdleState.ALL_IDLE) {
+                Log.i(TAG, "TcpClientHandler ===> ALL_IDLE");
+
             }
         }
     }
@@ -135,22 +135,27 @@ public class TcpClientHandler extends SimpleChannelInboundHandler<String> {
         totalRetryTimes++;
         System.out.println("TcpClientHandler 总计连接次数:"+totalRetryTimes);
         retryTimes++;
-        if (retryTimes>30){
-            //超时3次,其它处理
-            System.out.println("TcpClientHandler 重新连接"+(retryTimes-1)+"次,结束");
+        if (retryTimes > 30) { //超时30次,10分钟后再试
             retryTimes = 0;
-            //todo: 从API获取新的serverIP和serverPort,全新连接
-            //TcpClient.getInstance().init();
-            return;
-        }
+            ctx.channel().eventLoop().schedule(new Runnable() {
+                @Override
+                public void run() {
+                    System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次");
+                    TcpClient.getInstance().doConnect();
+                    reConnect(ctx);
+                }
+            }, 10*60, TimeUnit.SECONDS);
 
-        ctx.channel().eventLoop().schedule(new Runnable() {
-            @Override
-            public void run() {
-                System.out.println("TcpClientHandler 重新连接,第"+retryTimes+"次");
-                TcpClient.getInstance().doConnect();
-                reConnect(ctx);
-            }
-        },retrySeconds*retryTimes, TimeUnit.SECONDS);
+
+        }else {
+            ctx.channel().eventLoop().schedule(new Runnable() {
+                @Override
+                public void run() {
+                    System.out.println("TcpClientHandler 重新连接,第" + retryTimes + "次");
+                    TcpClient.getInstance().doConnect();
+                    reConnect(ctx);
+                }
+            }, retrySeconds * retryTimes, TimeUnit.SECONDS);
+        }
     }
 }

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

@@ -89,13 +89,7 @@ public class DeviceChannel {
                 }
                 break;
             case DEVICE:
-                if (tcpModel.getAction()== TcpAction.DeviceAction.RESTART){
-                    //todo:重启设备
-                    EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
-                } else if (tcpModel.getAction()== TcpAction.DeviceAction.APP_UPDATE) {
-                    //检查APP版本
-                    EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
-                }
+                EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));
                 break;
             case BROADCAST:
                 EventBus.getDefault().post(new MessageEvent(tcpModel, EVENT_TCP_MSG));

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

@@ -171,7 +171,10 @@ public interface TcpAction {
         CONNECT("连接"),
         APP_UPDATE("APP更新"),
         DEVICE_REFRESH("设备刷新"),
+        SYSTEM_SETTING("系统设置"),
+        DEVICE_CHANGE("设备更换"),
         USER_CHANGE("用户绑定");
+
         private String description;
         DeviceAction(String description){
             this.description = description;

+ 17 - 10
webrtc/src/main/java/com/wdkl/core/socket/SocketManager.java

@@ -11,6 +11,7 @@ import com.wdkl.core.voip.CallSingleActivity;
 import com.wdkl.core.voip.Utils;
 import com.wdkl.core.voip.VoipEvent;
 import com.wdkl.core.voip.VoipReceiver;
+import com.wdkl.ncs.android.middleware.common.Constant;
 import com.wdkl.ncs.android.middleware.common.MessageEvent;
 import com.wdkl.skywebrtc.CallSession;
 import com.wdkl.skywebrtc.EnumType;
@@ -146,8 +147,6 @@ public class SocketManager implements IEvent {
         if (webSocket != null) {
             webSocket.sendLeave(myId, room, userId);
         }
-
-        //EventBus.getDefault().post(new MessageEvent("handoff", EVENT_CALL_STATE));
     }
 
     public void sendRingBack(String targetId, String room) {
@@ -160,24 +159,18 @@ public class SocketManager implements IEvent {
         if (webSocket != null) {
             webSocket.sendRefuse(room, inviteId, myId, refuseType);
         }
-
-        //EventBus.getDefault().post(new MessageEvent("reject", EVENT_CALL_STATE));
     }
 
     public void sendCancel(String mRoomId, List<String> userIds) {
         if (webSocket != null) {
             webSocket.sendCancel(mRoomId, myId, userIds);
         }
-
-        //EventBus.getDefault().post(new MessageEvent("cancel", EVENT_CALL_STATE));
     }
 
     public void sendJoin(String room) {
         if (webSocket != null) {
             webSocket.sendJoin(room, myId);
         }
-
-        //EventBus.getDefault().post(new MessageEvent("accept", EVENT_CALL_STATE));
     }
 
     public void sendMeetingInvite(String userList) {
@@ -236,7 +229,11 @@ public class SocketManager implements IEvent {
         SkyEngineKit.init(new VoipEvent());
         boolean b = SkyEngineKit.Instance().startInCall(mContext, room, inviteId, audioOnly);
         if (b) {
-            CallSingleActivity.openActivity(mContext, inviteId, false, inviteId, audioOnly, false);
+            if (audioOnly) {
+                EventBus.getDefault().post(new MessageEvent("audio_call", Constant.EVENT_SIP_CALL_STATUS));
+            } else {
+                EventBus.getDefault().post(new MessageEvent("video_call", Constant.EVENT_SIP_CALL_STATUS));
+            }
         }
     }
 
@@ -348,6 +345,7 @@ public class SocketManager implements IEvent {
         if (iUserState != null && iUserState.get() != null) {
             iUserState.get().userLogout();
         }
+        unConnect();
     }
 
     @Override
@@ -373,9 +371,18 @@ public class SocketManager implements IEvent {
     @Override
     public void reConnect() {
         handler.post(() -> {
-            webSocket.reconnect();
+            if (webSocket != null) {
+                webSocket.reconnect();
+            }
         });
     }
+
+    public boolean socketOpen() {
+        if (webSocket != null) {
+            return webSocket.isOpen();
+        }
+        return false;
+    }
     //===========================================================================================
 
 

+ 1 - 0
webrtc/src/main/java/com/wdkl/core/voip/AsyncPlayer.java

@@ -78,6 +78,7 @@ public class AsyncPlayer {
 
                         if (mPlayer != null) {
                             mPlayer.stop();
+                            mPlayer.reset();
                             mPlayer.release();
                             mPlayer = null;
                         } else {