Prechádzať zdrojové kódy

## [1.0.3] version 3 - 2021-08-17
### CHANGE
- 解决界面切换会跳的问题
- 语音通话及视频通话集成到一个界面,并优化通话流程

weizhengliang 3 rokov pred
rodič
commit
517f8ef3da

+ 2 - 2
build.gradle

@@ -47,12 +47,12 @@ buildscript {
     /**
      * APP版本码
      */
-    ext.app_version_code = 1
+    ext.app_version_code = 3
 
     /**
      * APP版本号
      */
-    ext.app_version = "1.2"
+    ext.app_version = "1.0.3"
 
     /**
      * 项目依赖库

+ 7 - 1
common/src/main/code/com/wdkl/ncs/android/lib/base/BaseActivity.kt

@@ -94,7 +94,7 @@ abstract class BaseActivity<PresenterType : BaseContract.BasePresenter, DataBind
         setContentView(rootView)
 
         //解决全屏显示时弹出界面会跳一下的问题
-        window.decorView.systemUiVisibility = FULL_SCREEN_FLAG
+        //window.decorView.systemUiVisibility = FULL_SCREEN_FLAG
 
         /**执行抽象方法初始化Dagger相应操作*/
         bindDagger()
@@ -116,6 +116,12 @@ abstract class BaseActivity<PresenterType : BaseContract.BasePresenter, DataBind
         super.onResume()
         /**执行生命周期监听*/
         lifeCycleDo(LIFE_CYCLE_RESUME)
+        window.decorView.systemUiVisibility = FULL_SCREEN_FLAG
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        window.decorView.systemUiVisibility = FULL_SCREEN_FLAG
     }
 
     override fun onStart() {

+ 9 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt

@@ -87,6 +87,9 @@ class Constants {
         //紧急呼叫
         val EVENT_SOS_CALL = 0x06
 
+        //退出通话界面
+        val EVENT_REMOVE_CALL_FRAGMENT = 0x07
+
         //手柄拿起
         val HOOK_OFF = "com.android.PhoneWinowManager.HOOK_OFF"
         //手柄放下
@@ -109,6 +112,12 @@ class Constants {
         //紧急呼叫
         val CALL_SOS = 4
 
+        //探视
+        val CALL_VISITING = 5
+
+        //探视
+        val CALL_VISIT_CALLING = 6
+
         //通话状态
         var CALL_STATE = CALL_STANDBY
     }

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

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

+ 142 - 37
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt

@@ -9,6 +9,7 @@ import android.graphics.Color
 import android.net.ConnectivityManager
 import android.net.Uri
 import android.os.Build
+import android.os.Bundle
 import android.os.CountDownTimer
 import android.provider.Settings
 import android.support.v4.app.Fragment
@@ -24,12 +25,10 @@ import com.google.gson.Gson
 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.core.voip.SpeechUtil
 import com.wdkl.core.window.VisitingWindow
 import com.wdkl.ncs.android.component.nursehome.BuildConfig
 import com.wdkl.ncs.android.component.nursehome.R
-import com.wdkl.ncs.android.component.nursehome.SipUtil.SipCallBack
 import com.wdkl.ncs.android.component.nursehome.common.Constants
 import com.wdkl.ncs.android.component.nursehome.databinding.ActivityNurseHomeBinding
 import com.wdkl.ncs.android.component.nursehome.fragment.*
@@ -83,6 +82,10 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
     var currentFragmentTwo:Fragment? = null
     var currentFragmentThree:Fragment? = null
 
+    //通话界面fragment
+    private var skyCallFragment: Fragment? = null
+    private var targetId: String? = null
+
     var receiver :TimeReceiver? = null
 
     lateinit var countDownTimer: CountDownTimer
@@ -531,6 +534,24 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
         }
     }
 
+    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
+        }
+    }
+
 
     inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
         beginTransaction().func().commit()
@@ -558,6 +579,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
             } else if (intent.action == Constants.HOOK_ON) {
                 Log.e(TAG,"手柄放下 ")
                 Constants.hookOn = true
+                VoiceManagerUtil.switchAudioMode(activity, true)
                 if (Constants.CALL_STATE == Constants.CALL_OUTGOING) {
                     //呼出取消
                     Constants.CALL_STATE = Constants.CALL_STANDBY
@@ -571,16 +593,23 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                     DeviceChannel.calling = false
                     VoiceUtil.rejectAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
                     CallDialogHelper.dismissCallDialog()
+                } else if (Constants.CALL_STATE == Constants.CALL_CALLING) {
+                    Constants.CALL_STATE = Constants.CALL_STANDBY
+                    DeviceChannel.calling = false
+                    SocketManager.getInstance().endCall()
                 }
             } else if (intent.action == Constants.HOOK_OFF) {
                 Log.e(TAG,"手柄拿起 ")
                 Constants.hookOn = false
+                VoiceManagerUtil.switchAudioMode(activity, false)
                 if (Constants.CALL_STATE == Constants.CALL_INCOMING) {
                     //来电接听
                     Constants.CALL_STATE = Constants.CALL_CALLING
                     DeviceChannel.calling = true
                     VoiceUtil.acceptAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
                     CallDialogHelper.dismissCallDialog()
+                } else if (Constants.CALL_STATE == Constants.CALL_VISITING) {
+                    acceptVisit()
                 }
             } else if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
                 updateNetState()
@@ -681,12 +710,12 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
             wifi_state_imagev.visibility = View.GONE
         }
 
-        if (NetHelper.isBTConnected()) {
+        /*if (NetHelper.isBTConnected()) {
             bluetooth_state_imagev.visibility = View.VISIBLE
             bluetooth_state_imagev.setImageResource(R.drawable.ic_bt_success)
         } else {
             bluetooth_state_imagev.visibility = View.GONE
-        }
+        }*/
     }
 
     private fun updateTcpState() {
@@ -710,9 +739,44 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
         //继续执行父类其他点击事件
     }
 
+    //探视接听
+    fun acceptVisit() {
+        CallDialogHelper.dismissCallDialog()
+        //发送tcp,同时发起视频通话
+        VideoUtil.sendVideoInCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
+
+        Constants.CALL_STATE = Constants.CALL_VISIT_CALLING
+        DeviceChannel.calling = true
+        //通话之前先判断webrtc socket是否连接上,否则不能建立通话
+        if (SocketManager.getInstance().socketOpen()) {
+            var fragment = SkyCallFragment()
+            var bundle = Bundle()
+            bundle.putBoolean("audio_only", false)
+            bundle.putInt("call_state", 0)
+            bundle.putInt("fromId", Constants.fromId!!)
+            bundle.putInt("interactionId",  Constants.interactionId!!)
+            bundle.putString("targetId", targetId)
+            bundle.putBoolean("visiting", true)
+            fragment.arguments = bundle
+            addCallFragment(fragment)
+        } else {
+            showMessage("通话服务还未建立连接,请稍后")
+            Constants.CALL_STATE = Constants.CALL_STANDBY
+            DeviceChannel.calling = false
+            VoiceUtil.rejectAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
+        }
+    }
+
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
         when (messageEvent.getType()) {
+            //退出通话界面
+            Constants.EVENT_REMOVE_CALL_FRAGMENT -> {
+                if (skyCallFragment != null) {
+                    removeCallFragment()
+                }
+            }
+
             Constants.EVENT_TCP_MSG -> {
                 var tcpModel = messageEvent.getMessage() as TcpModel
                 Log.e(TAG, "收到tcp消息" + tcpModel.getType() + " " + tcpModel.getAction())
@@ -729,15 +793,15 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                             val text = "来自: " + interactionVO.fromFrameFullName + " 的通话请求"
                             CallDialogHelper.dismissCallDialog()
                             CallDialogHelper.showCallDialog(this@NurseHomeActivity, 1, text,
-                                View.OnClickListener {
+                                {
                                     //呼出取消
                                 },
-                                View.OnClickListener {
+                                {
                                     //来电接听
                                     VoiceUtil.acceptAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
                                     CallDialogHelper.dismissCallDialog()
                                 },
-                                View.OnClickListener {
+                                {
                                     //来电拒接
                                     DeviceChannel.calling = false
                                     VoiceUtil.rejectAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
@@ -754,16 +818,39 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                         Log.e(TAG, "对方接受语音" + tcpModel.toJson())
                         Constants.interactionId = interactionVO.id
                         Constants.fromId = tcpModel.fromId
+                        targetId = interactionVO.toSipId
                         CallDialogHelper.dismissCallDialog()
                         countDownTimer.cancel()
 
                         DeviceChannel.calling = true
                         Constants.CALL_STATE = Constants.CALL_CALLING
-                        if (Constants.call_type == 1) {
+
+                        /*if (Constants.call_type == 1) {
                             CallSingleActivity.openActivity(activity, interactionVO.toSipId, true, interactionVO.toSipId, false, false, false)
                         } else {
                             CallSingleActivity.openActivity(activity, interactionVO.toSipId, true, interactionVO.toSipId, true, false, false)
+                        }*/
+
+
+                        //通话之前先判断webrtc socket是否连接上,否则不能建立通话
+                        if (SocketManager.getInstance().socketOpen()) {
+                            var fragment = SkyCallFragment()
+                            var bundle = Bundle()
+                            bundle.putBoolean("audio_only", Constants.call_type == 0)
+                            bundle.putInt("call_state", 0)
+                            bundle.putInt("fromId", tcpModel.fromId)
+                            bundle.putInt("interactionId", interactionVO.id)
+                            bundle.putBoolean("visiting", false)
+                            bundle.putString("targetId", interactionVO.toSipId)
+                            fragment.arguments = bundle
+                            addCallFragment(fragment)
+                        } else {
+                            showMessage("通话服务还未建立连接,请稍后")
+                            Constants.CALL_STATE = Constants.CALL_STANDBY
+                            DeviceChannel.calling = false
+                            VoiceUtil.rejectAudioCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
                         }
+
                     } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {//对方拒绝
                         Constants.CALL_STATE = Constants.CALL_STANDBY
                         DeviceChannel.calling = false
@@ -792,23 +879,27 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                         CallDialogHelper.dismissCallDialog()
                         countDownTimer.cancel()
                     } else if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {//服务器返回的呼叫成功tcp
+                        Constants.interactionId = interactionVO.id
                         callTargetId = interactionVO.toDeviceId
                         countDownTimer.start()
                         Constants.CALL_STATE = Constants.CALL_OUTGOING
 
                         CallDialogHelper.dismissCallDialog()
-                        CallDialogHelper.showCallDialog(this@NurseHomeActivity, 0, "正在呼叫,等待接听中...", View.OnClickListener {
-                            //呼出取消
-                            Constants.CALL_STATE = Constants.CALL_STANDBY
-                            DeviceChannel.calling = false
-                            VoiceUtil.cancelAudioCall(Integer.parseInt(Constants.ids), callTargetId)
-                            CallDialogHelper.dismissCallDialog()
-                            countDownTimer.cancel()
-                        }, View.OnClickListener {
-                            //来电接听
-                        }, View.OnClickListener {
-                            //来电拒接
-                        })
+                        CallDialogHelper.showCallDialog(this@NurseHomeActivity, 0, "正在呼叫,等待接听中...",
+                            {
+                                //呼出取消
+                                Constants.CALL_STATE = Constants.CALL_STANDBY
+                                DeviceChannel.calling = false
+                                VoiceUtil.cancelAudioCall(Integer.parseInt(Constants.ids), callTargetId)
+                                CallDialogHelper.dismissCallDialog()
+                                countDownTimer.cancel()
+                            },
+                            {
+                                //来电接听
+                            },
+                            {
+                                //来电拒接
+                            })
 
                         EventBus.getDefault().post(MessageEvent(0, Constants.EVENT_REFRESH_CALL_LIST))
                     }
@@ -856,26 +947,17 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                         //收到探视请求,弹出提示
                         Constants.fromId = tcpModel.fromId
                         Constants.interactionId = interactionVO.id
-                        Constants.CALL_STATE = Constants.CALL_INCOMING
+                        Constants.CALL_STATE = Constants.CALL_VISITING
+                        //因为探视请求是探视机发起的,所以这里发起通话的时候对方设备应该是探视机的sipId,也即是fromSipId
+                        targetId = interactionVO.fromSipId
                         DeviceChannel.calling = true
 
                         CallDialogHelper.dismissCallDialog()
-                        CallDialogHelper.showCallDialog(this@NurseHomeActivity, 1, "您有新的探视请求", View.OnClickListener {
+                        CallDialogHelper.showCallDialog(this@NurseHomeActivity, 1, "您有新的探视请求", {
                             //呼出取消
-                        }, View.OnClickListener {
-                            //接受探视,创建多人视频房间
-                            //VideoUtil.acceptVideoCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
-                            //VideoUtil.sendVideoInCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
-                            CallDialogHelper.dismissCallDialog()
-
-                            //发送tcp,同时发起视频通话
-                            VideoUtil.sendVideoInCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
-                            CallSingleActivity.openActivity(activity, interactionVO.fromSipId, true, interactionVO.fromSipId, false, false, true)
-
-                            // 创建一个房间并进入
-                            //val roomId = "visit-room-" + Constants.interactionId
-                            //CallMultiActivity.openActivity(activity, roomId, true)
-                        }, View.OnClickListener {
+                        }, {
+                            acceptVisit()
+                        }, {
                             //拒接探视
                             DeviceChannel.calling = false
                             VideoUtil.rejectVideoCall(Integer.parseInt(Constants.ids), Constants.fromId, Constants.interactionId)
@@ -895,7 +977,7 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                         VisitingWindow.release()
                     } else if (tcpModel.action == TcpAction.VideoAction.SUCCESS) {
                         //显示探视窗口
-                        Constants.CALL_STATE = Constants.CALL_STANDBY
+                        Constants.CALL_STATE = Constants.CALL_VISITING
                         DeviceChannel.calling = true
                         VisitingWindow.createFloatView(activity, Constants.visit_bed_name)
                     } else if (tcpModel.action == TcpAction.VideoAction.FAILED) {
@@ -908,6 +990,11 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                         DeviceChannel.calling = false
                         CallDialogHelper.dismissCallDialog()
                         showMessage("分机正在通话中,请稍后再试...")
+                    } else if (tcpModel.action == TcpAction.VideoAction.REJECT) {
+                        Constants.CALL_STATE = Constants.CALL_STANDBY
+                        DeviceChannel.calling = false
+                        CallDialogHelper.dismissCallDialog()
+                        showMessage("通话异常,请稍后再试...")
                     }
                 } else if (tcpModel.type == TcpType.DEVICE) {
                     if (tcpModel.action == TcpAction.DeviceAction.SYSTEM_SETTING) {
@@ -944,6 +1031,24 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
                     }
                 }
             }
+
+            //对方发起sip通话请求,我方接受
+            Constants.EVENT_CALL_STATE -> {
+                if (messageEvent.getMessage() is String) {
+                    val call = messageEvent.getMessage() as String
+                    var fragment = SkyCallFragment()
+                    var bundle = Bundle()
+                    bundle.putInt("call_state", 1)
+                    bundle.putBoolean("visiting", false)
+                    if (call.equals("video_call")) {
+                        bundle.putBoolean("audio_only", false)
+                    } else {
+                        bundle.putBoolean("audio_only", true)
+                    }
+                    fragment.arguments = bundle
+                    addCallFragment(fragment)
+                }
+            }
         }
 
     }

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

@@ -0,0 +1,110 @@
+package com.wdkl.ncs.android.component.nursehome.fragment
+
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.enation.javashop.utils.base.tool.BaseToolActivity
+import com.wdkl.core.voip.VoipEvent
+import com.wdkl.ncs.android.component.nursehome.common.Constants
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.utils.MessageEvent
+import com.wdkl.skywebrtc.SkyEngineKit
+import com.wdkl.skywebrtc.except.NotInitializedException
+import org.greenrobot.eventbus.EventBus
+
+abstract class BaseCallFragment: Fragment() {
+
+    private var layout: View? = null
+
+    protected lateinit var baseActivity: BaseToolActivity
+
+    //通话状态:0-去电, 1-来电
+    protected var callState : Int = 0
+    protected var onlyAudio: Boolean = true
+    //来电设备id
+    protected var fromId: Int = -1
+    protected var interactionId: Int? = -1
+    protected var targetId: String? = null
+    //是否探视
+    protected var visiting: Boolean = false
+
+    protected var gEngineKit: SkyEngineKit? = null
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        retainInstance = true
+        callState = arguments.getInt("call_state")
+        onlyAudio = arguments.getBoolean("audio_only")
+        fromId = arguments.getInt("fromId")
+        interactionId = arguments.getInt("interactionId")
+        targetId = arguments.getString("targetId")
+        visiting = arguments.getBoolean("visiting")
+
+    }
+
+    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 backToMain() {
+        EventBus.getDefault().post(MessageEvent("BackCall", Constants.EVENT_REMOVE_CALL_FRAGMENT))
+    }
+
+}

+ 380 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/SkyCallFragment.kt

@@ -0,0 +1,380 @@
+package com.wdkl.ncs.android.component.nursehome.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.alibaba.android.vlayout.VirtualLayoutManager
+import com.google.gson.Gson
+import com.wdkl.core.adapter.BedItemAdapter
+import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.common.Constants
+import com.wdkl.ncs.android.component.nursehome.util.VoiceManagerUtil
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.tcp.channel.VideoUtil
+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.ncs.android.middleware.utils.CommonUtils
+import com.wdkl.ncs.android.middleware.utils.MessageEvent
+import com.wdkl.skywebrtc.CallSession
+import com.wdkl.skywebrtc.EnumType
+import com.wdkl.skywebrtc.SkyEngineKit
+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 {
+
+    private var localSurfaceView: SurfaceViewRenderer? = null
+    private var remoteSurfaceView: SurfaceViewRenderer? = null
+
+    private var bedItemAdapter: BedItemAdapter? = null
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    override fun getLayId(): Int {
+        return R.layout.sky_voice_call_layout
+    }
+
+    override fun init() {
+        acceptCall()
+        if (visiting) {
+            bedItemAdapter = BedItemAdapter(baseActivity, CommonUtils.getInBedVOS())
+            visit_list_view.setAdapter(bedItemAdapter)
+            visit_list_view.setLayoutManager(VirtualLayoutManager(baseActivity))
+            visit_list_view.setVisibility(View.VISIBLE)
+            fullscreen_video_frame.setOnClickListener(View.OnClickListener {
+                if (visit_list_view.getVisibility() == View.GONE) {
+                    visit_list_view.setVisibility(View.VISIBLE)
+                } else {
+                    visit_list_view.setVisibility(View.GONE)
+                }
+            })
+
+            bindListener()
+        }
+
+        when (callState) {
+            0 -> {
+                //发起通话
+                if (!startCall(targetId!!, onlyAudio)) {
+                    //通话失败,重置并返回主界面
+                    Constants.CALL_STATE = Constants.CALL_STANDBY
+                    DeviceChannel.calling = false
+                    VoiceUtil.handoffAudioCall(Integer.parseInt(Constants.ids), fromId, interactionId)
+                    if (sky_voice_call_timer != null) {
+                        sky_voice_call_timer.stop()
+                    }
+                    backToMain()
+                }
+            }
+
+            1 -> {
+                //接受通话
+                if (onlyAudio) {
+                    Handler().postDelayed({
+                        joinAudioCall()
+                    }, 1500)
+                } else {
+                    Handler().postDelayed({
+                        joinVideoCall()
+                    }, 2000)
+                }
+            }
+        }
+    }
+
+    private fun bindListener() {
+        bedItemAdapter!!.setInvitClickListener { bedVO ->
+            Constants.visitedId = bedVO.bedDeviceId
+            visit_list_view.setVisibility(View.GONE)
+            Constants.visit_bed_name = bedVO.frameBed.fullName
+
+            //延迟发送
+            Handler().postDelayed({ //发送探视邀请给分机
+                VideoUtil.sendInviteVideoCall(Integer.parseInt(Constants.ids),
+                    bedVO.bedDeviceId,
+                    Constants.interactionId)
+            }, 2000)
+
+            //关闭主机和探视机的视频,然后由分机向探视机发视频通话
+            if (gEngineKit!!.currentSession != null) {
+                Log.d("dds", "endCall")
+                SkyEngineKit.Instance().endCall()
+            }
+
+            backToMain()
+        }
+    }
+
+    override fun bindEvent() {
+        //通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            //结束sip通话
+            val session = gEngineKit?.getCurrentSession()
+            if (session != null) {
+                session.leave()
+            }
+
+            Constants.CALL_STATE = Constants.CALL_STANDBY
+            DeviceChannel.calling = false
+            sky_voice_call_timer.stop()
+            backToMain()
+        }
+    }
+
+    override fun destroy() {
+        Constants.CALL_STATE = Constants.CALL_STANDBY
+        DeviceChannel.calling = false
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+    }
+
+    //开始接听
+    private fun acceptCall() {
+        sky_voice_call_calling_text.text = "连接中..."
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+    }
+
+    //语音接通
+    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
+        }
+
+        Constants.CALL_STATE = Constants.CALL_CALLING
+        DeviceChannel.calling = true
+        sky_voice_call_calling_text.text = "通话中..."
+        sky_voice_call_timer.visibility = View.VISIBLE
+        sky_voice_call_timer.base = SystemClock.elapsedRealtime()
+        sky_voice_call_timer.start()
+
+        if (Constants.hookOn) {
+            //手柄放下,免提模式
+            VoiceManagerUtil.switchAudioMode(activity, true)
+        } else {
+            //手柄拿起,听筒模式
+            VoiceManagerUtil.switchAudioMode(activity, false)
+        }
+    }
+
+    //创建会话
+    private fun startCall(targetId: String, audioOnly: Boolean): Boolean {
+        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 !!!!!!!!!!!!!!!!!!")
+        Constants.CALL_STATE = Constants.CALL_STANDBY
+        DeviceChannel.calling = false
+
+        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(onlyAudio)
+            }
+        }
+    }
+
+    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.getType()) {
+            Constants.EVENT_TCP_MSG -> {
+                if (messageEvent.getMessage() is TcpModel) {
+                    val curTcpModel = messageEvent.getMessage() as TcpModel
+                    if (curTcpModel.getType() == TcpType.VOICE) {
+                        val curInteractionVO = Gson().fromJson(curTcpModel.data.toString(), InteractionVO::class.java)
+                        if (curTcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) {
+                            //对方挂断,不论我方呼出或呼入
+                            if (Constants.interactionId == curInteractionVO.id) {
+                                callEnd()
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 10 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/VoiceManagerUtil.java

@@ -181,4 +181,14 @@ public class VoiceManagerUtil {
         audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, (int) (getCallMax(context) * vPercent), 0);
     }
 
+    public static void switchAudioMode(Context context, boolean speakerOn) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setSpeakerphoneOn(speakerOn);
+        if (speakerOn) {
+            audioManager.setMode(AudioManager.MODE_NORMAL);
+        } else {
+            audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        }
+    }
+
 }

+ 7 - 0
nursehome/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
nursehome/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
nursehome/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
nursehome/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>

+ 9 - 10
nursehome/src/main/res/layout/activity_nurse_home.xml

@@ -1,13 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:tools="http://schemas.android.com/tools">
 
-    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        tools:context=".activity.HelloActivity"
-        tools:ignore="MissingConstraints">
-
-        <RelativeLayout
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:background="@color/javashop_color_white">
@@ -44,6 +38,7 @@
                     android:layout_centerVertical="true"
                     android:format12Hour="yyyy-MM-dd HH:mm:ss EEEE"
                     android:format24Hour="yyyy-MM-dd HH:mm:ss EEEE"
+                    android:timeZone="GMT+8"
                     android:textColor="#2F9DF1"
                     android:textSize="16sp" />
 
@@ -295,10 +290,14 @@
 
                     </RadioGroup>
                 </LinearLayout>
-
             </LinearLayout>
 
-        </RelativeLayout>
+        <!--通话界面-->
+        <FrameLayout
+            android:id="@+id/call_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+    </RelativeLayout>
 
-    </android.support.constraint.ConstraintLayout>
 </layout>

+ 82 - 0
nursehome/src/main/res/layout/sky_voice_call_layout.xml

@@ -0,0 +1,82 @@
+<?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">
+
+            <android.support.v7.widget.RecyclerView
+                android:id="@+id/visit_list_view"
+                android:layout_width="240dp"
+                android:layout_height="match_parent"
+                android:background="@color/color_transparent"
+                android:visibility="gone"/>
+
+            <LinearLayout
+                android:id="@+id/ll_voice_call"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="240dp"
+                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:gravity="center"
+                    android:text="连接中..."
+                    android:textColor="#9E9E9F"
+                    android:textSize="32sp" />
+            </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="80dp"
+                    android:layout_height="80dp"
+                    android:layout_marginTop="20dp"
+                    android:src="@drawable/selector_call_hangup" />
+            </LinearLayout>
+        </RelativeLayout>
+    </FrameLayout>
+</layout>

+ 6 - 0
readme.md

@@ -26,4 +26,10 @@
 
 ---
 
+## [1.0.3] version 3 - 2021-08-17
+### CHANGE
+- 解决界面切换会跳的问题
+- 语音通话及视频通话集成到一个界面,并优化通话流程
+
+
 ## 从ncs_callingmain_temp仓库迁移过来,用于开发卡尔和3128两种设备护士主机

+ 8 - 1
webrtc/src/main/java/com/wdkl/core/socket/SocketManager.java

@@ -11,6 +11,8 @@ 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.component.nursehome.common.Constants;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
 import com.wdkl.skywebrtc.CallSession;
 import com.wdkl.skywebrtc.EnumType;
 import com.wdkl.skywebrtc.SkyEngineKit;
@@ -223,7 +225,12 @@ 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, false);
+            if (audioOnly) {
+                EventBus.getDefault().post(new MessageEvent("audio_call", Constants.Companion.getEVENT_CALL_STATE()));
+            } else {
+                EventBus.getDefault().post(new MessageEvent("video_call", Constants.Companion.getEVENT_CALL_STATE()));
+            }
+            //CallSingleActivity.openActivity(mContext, inviteId, false, inviteId, audioOnly, false, false);
         }
     }
 

+ 2 - 2
webrtc/src/main/java/com/wdkl/core/voip/FragmentAudio.java

@@ -67,11 +67,11 @@ public class FragmentAudio extends SingleCallFragment implements View.OnClickLis
             // 如果未接通
             descTextView.setText("正在建立连接...");
             if (isOutgoing) {
-                descTextView.setText(R.string.av_waiting);
+                //descTextView.setText(R.string.av_waiting);
                 outgoingActionContainer.setVisibility(View.VISIBLE);
                 incomingActionContainer.setVisibility(View.GONE);
             } else {
-                descTextView.setText(R.string.av_audio_invite);
+                //descTextView.setText(R.string.av_audio_invite);
                 outgoingActionContainer.setVisibility(View.GONE);
                 incomingActionContainer.setVisibility(View.VISIBLE);
 

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

@@ -144,7 +144,7 @@ public class FragmentVideo extends SingleCallFragment implements View.OnClickLis
                 Constants.Companion.setVisitedId(bedVO.getBedDeviceId());
                 recyclerView.setVisibility(View.GONE);
                 //Toast.makeText(callSingleActivity, "已发送探视邀请,请稍后...", Toast.LENGTH_LONG).show();
-                DeviceChannel.calling = false;
+                //DeviceChannel.calling = false;
                 Constants.Companion.setVisit_bed_name(bedVO.getFrameBed().getFullName());
 
                 //延迟发送

+ 1 - 1
webrtc/src/main/res/layout/fragment_video.xml

@@ -28,7 +28,7 @@
 
         <android.support.v7.widget.RecyclerView
             android:id="@+id/visit_list_view"
-            android:layout_width="200dp"
+            android:layout_width="240dp"
             android:layout_height="match_parent"
             android:background="@color/color_transparent"
             android:visibility="gone"/>