소스 검색

集成sip通话功能

weizhengliang 3 년 전
부모
커밋
b1b9a204d9

+ 10 - 0
build.gradle

@@ -106,6 +106,11 @@ buildscript {
         google()
 
         maven { url "https://jitpack.io" }
+
+        maven {
+            // Replace snapshots by releases for releases !
+            url "https://linphone.org/maven_repository"
+        }
     }
 }
 /**
@@ -122,6 +127,11 @@ allprojects {
         google()
 
         maven { url "https://jitpack.io" }
+
+        maven {
+            // Replace snapshots by releases for releases !
+            url "https://linphone.org/maven_repository"
+        }
     }
     tasks.withType(Javadoc) { // 新增
         options.addStringOption('Xdoclint:none', '-quiet')

+ 3 - 0
callingdoor/build.gradle

@@ -88,6 +88,9 @@ dependencies {
 
     //compile project(':janus')
 
+    //linphone sip
+    implementation "org.linphone:linphone-sdk-android:4.3.3"
+
     if(componentTag){
         debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
         releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

+ 4 - 0
callingdoor/src/main/AndroidManifest.xml

@@ -22,6 +22,10 @@
             android:screenOrientation="landscape"
             android:launchMode="singleTask"/>
 
+        <service
+            android:name=".sip.WdklSipService"
+            android:label="@string/app_name" />
+
         <provider
             android:name="android.support.v4.content.FileProvider"
             android:authorities="${applicationId}.provider"

+ 75 - 16
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/activity/CallingdoorActivity.kt

@@ -19,6 +19,7 @@ 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.callingdoor.sip.WdklSipService
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.lib.utils.*
@@ -62,6 +63,9 @@ import okhttp3.Request
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
+import org.linphone.core.AccountCreator
+import org.linphone.core.RegistrationState
+import org.linphone.core.TransportType
 import serialporttest.utils.SerialPortUtil
 import java.io.*
 import java.util.*
@@ -90,6 +94,7 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
 
     //通话界面fragment
     private var skyCallFragment: Fragment? = null
+    private var mAccountCreator: AccountCreator? = null
 
     private var initialized :Boolean = false
     private var loaded: Boolean = false
@@ -141,6 +146,9 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
             Constant.DEVICE_REGISTER_ID = Constant.LOCAL_MAC
         }
 
+        //启动sip服务
+        startService(Intent().setClass(this, WdklSipService::class.java))
+
         //presenter.loadTcpServerHost()
         //注册广播
         regReceiver()
@@ -784,6 +792,21 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
 
         presenter.loadPartSettings(Constant.PART_ID)
 
+        //配置sip账户
+        if (WdklSipService.getCore() != null) {
+            mAccountCreator = WdklSipService.getCore().createAccountCreator(null)
+            // 以下三项必须
+            mAccountCreator!!.setDomain(Constant.TCP_SERVER_URL)
+            mAccountCreator!!.setUsername("3305")
+            mAccountCreator!!.setPassword("3305")
+            //默认使用udp
+            mAccountCreator!!.transport = TransportType.Udp
+
+            // 这里会自动创建代理配置、认证信息到 SIP核心
+            val cfg = mAccountCreator!!.createProxyConfig()
+            // 确保新创建的是最新
+            WdklSipService.getCore().defaultProxyConfig = cfg
+        }
 
         //检查版本
         Constant.silentUpdate = true
@@ -893,8 +916,6 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         receiver = TimeReceiver()
         var intentFilter = IntentFilter()
         intentFilter.addAction(Intent.ACTION_TIME_TICK)
-        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
-        intentFilter.addAction(Intent.ACTION_TIME_CHANGED)
         intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
         registerReceiver(receiver, intentFilter)
     }
@@ -953,7 +974,7 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
     }
 
     //开始呼叫
-    fun startCall(type: Int) {
+    private fun startCall(type: Int) {
         if (Constant.DEVICE_STATUS != 1) {
             showMessage("设备未启用,无法呼叫")
             return
@@ -962,13 +983,13 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         //通话之前先判断webrtc socket和tcp是否连接正常,否则不能建立通话
         if (Constant.TCP_CONNECTED) {
             //去电界面
-            /*Constant.CALL_TYPE = type
+            Constant.CALL_TYPE = type
             Constant.CALL_STATE = Constant.CALL_OUTGOING
-            var fragment = SkyCallFragment()
+            var fragment = SipCallFragment()
             var bundle = Bundle()
             bundle.putInt("call_state", 0)
             fragment.arguments = bundle
-            addCallFragment(fragment)*/
+            addCallFragment(fragment)
         } else {
             showMessage("通话服务或网络未连接,请检查网络稍后再试")
         }
@@ -981,12 +1002,12 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
             //去电界面
             Constant.CALL_TYPE = type
             Constant.CALL_STATE = Constant.CALL_OUTGOING
-            /*var fragment = SkyCallFragment()
+            var fragment = SipCallFragment()
             var bundle = Bundle()
             bundle.putInt("call_state", 2)
             bundle.putInt("bed_id", bedId)
             fragment.arguments = bundle
-            addCallFragment(fragment)*/
+            addCallFragment(fragment)
         } else {
             showMessage("通话服务或网络未连接,请检查网络稍后再试")
         }
@@ -997,10 +1018,16 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         when (messageEvent.getType()) {
             //Sip注册状态
             Constant.EVENT_SIP_REGISTER_STATUS -> {
-                if (messageEvent.message is String) run {
+                /*if (messageEvent.message is String) run {
                     val status = messageEvent.message as String
                     Log.d("sip", "sip regist status: " + status)
                     updateStatus(status)
+                }*/
+
+                if (messageEvent.message is RegistrationState) {
+                    val state = messageEvent.message as RegistrationState
+                    Log.d(TAG, "sip register state: $state")
+                    updateSipState(state)
                 }
             }
 
@@ -1027,12 +1054,12 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
                             Constant.CALL_STATE = Constant.CALL_INCOMING
                             if (Constant.TCP_CONNECTED && !TextUtils.isEmpty(Constant.SIP_ID)) {
                                 //来电界面
-                                /*var fragment = SkyCallFragment()
+                                var fragment = SipCallFragment()
                                 var bundle = Bundle()
                                 bundle.putInt("call_state", 1)
                                 bundle.putSerializable("tcp_model", tcpModel)
                                 fragment.arguments = bundle
-                                addCallFragment(fragment)*/
+                                addCallFragment(fragment)
                             } else {
                                 showMessage("服务未连接或sipId为空")
                                 Constant.CALL_STATE = Constant.CALL_STANDBY
@@ -1117,6 +1144,32 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
         }
     }
 
+    private fun updateSipState(state: RegistrationState) {
+        runOnUiThread {
+            when (state) {
+                RegistrationState.Ok -> {
+                    //连接完成
+                    view_title_layout_tv_point.setBackgroundResource(R.color.green)
+                }
+
+                RegistrationState.Failed -> {
+                    //连接错误
+                    view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
+                }
+
+                RegistrationState.Progress -> {
+                    //正在连接
+                    view_title_layout_tv_point.setBackgroundResource(R.color.yellow_color)
+                }
+
+                RegistrationState.None, RegistrationState.Cleared -> {
+                    //默认状态,断开连接
+                    view_title_layout_tv_point.setBackgroundResource(R.color.register_text_color)
+                }
+            }
+        }
+    }
+
     private fun updateNetState() {
         if (NetHelper.getInstance().networkType == ConnectivityManager.TYPE_WIFI) {
             view_title_layout_iv_wifi.visibility = View.VISIBLE
@@ -1172,10 +1225,10 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
     private fun updateTcpState() {
         if (Constant.TCP_CONNECTED) {
             view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_success)
-            view_title_layout_tv_point.setBackgroundResource(R.color.green)
+            //view_title_layout_tv_point.setBackgroundResource(R.color.green)
         } else {
             view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_fail)
-            view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
+            //view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
         }
     }
 
@@ -1234,9 +1287,15 @@ class CallingdoorActivity :BaseActivity<CallingdoorActivityPresenter, Callingdoo
 
     inner class TimeReceiver: BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
-            if (intent.action == Intent.ACTION_TIME_TICK
-                    || intent.action == Intent.ACTION_TIME_CHANGED
-                    || intent.action == Intent.ACTION_TIMEZONE_CHANGED) {
+            if (intent.action == Intent.ACTION_TIME_TICK) {
+                //更新sip注册状态
+                if (WdklSipService.getCore() != null) {
+                    val proxyConfig = WdklSipService.getCore().defaultProxyConfig
+                    if (proxyConfig != null) {
+                        updateSipState(proxyConfig.state)
+                    }
+                }
+
                 updateNetState()
                 if (initialized) {
                     updateSettings(false)

+ 401 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/fragment/SipCallFragment.kt

@@ -0,0 +1,401 @@
+package com.wdkl.app.ncs.callingdoor.fragment
+
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import com.google.gson.Gson
+import com.wdkl.app.ncs.callingdoor.R
+import com.wdkl.app.ncs.callingdoor.helper.RingPlayHelper
+import com.wdkl.app.ncs.callingdoor.helper.SerialPortHelper
+import com.wdkl.app.ncs.callingdoor.sip.WdklSipService
+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 kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import org.linphone.core.Call
+import org.linphone.core.Core
+import org.linphone.core.CoreListenerStub
+
+class SipCallFragment: BaseCallFragment() {
+    private val TAG = "SkyCallFragment"
+
+    //来电设备id
+    var fromId: Int = -1
+
+    private var interactionVO: InteractionVO? = null
+    private var sipCore: Core? = null
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    private var audioCall: Boolean = true
+
+    private var callEnded: Boolean = false
+
+    private var outGoing: Boolean = false
+
+    // 配置通话状态监听
+    private val coreListener = object : CoreListenerStub() {
+        override fun onCallStateChanged(
+            core: Core,
+            call: Call,
+            state: Call.State,
+            message: String
+        ) {
+            if (state == Call.State.IncomingReceived) {
+                //来电时将自动接听
+                if (sipCore != null) {
+                    val params = sipCore!!.createCallParams(call)
+                    params.enableVideo(false)
+                    call.acceptWithParams(params)
+                } else {
+                    callEnd(true)
+                }
+            } else if (state == Call.State.End || state == Call.State.Released) {
+                callEnd(false)
+            } else if (state == Call.State.Connected) {
+                showCalling(true)
+            }
+        }
+    }
+
+    override fun getLayId(): Int {
+        if ("rk3288".equals(Build.MODEL)) {
+            return R.layout.sky_voice_call_layout_rk3288
+        } else {
+            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)
+        }
+
+        sipCore = WdklSipService.getCore()
+        if (sipCore != null) {
+            sipCore!!.addListener(coreListener)
+        }
+
+        when (callState) {
+            0 -> {
+                //发起通话
+                outGoing = true
+                //呼叫主机
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_back2, true)
+                if (!startOutgoing()) {
+                    AppTool.Time.delay(3000) {
+                        cancelCall()
+                    }
+                }
+            }
+
+            1 -> {
+                //来电
+                outGoing = false
+                showIncomingCall()
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_tone, true)
+            }
+
+            2 -> {
+                //呼叫分机
+                outGoing = true
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_back2, true)
+                if (!startOutgoing()) {
+                    AppTool.Time.delay(3000) {
+                        cancelCall()
+                    }
+                }
+            }
+        }
+
+    }
+
+    override fun bindEvent() {
+        //去电取消或通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                //结束sip通话
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                if (sky_voice_call_timer != null) {
+                    sky_voice_call_timer.stop()
+                }
+                callTerminate()
+                callEnd(true)
+            } else {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                if (callState == 0) {
+                    VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                } else if (callState == 2) {
+                    if (bedId != -1) {
+                        VoiceUtil.cancelAudioCallBed(Constant.DEVICE_ID, bedId)
+                    }
+                }
+                cancelCall()
+            }
+        }
+
+        //来电拒绝
+        sky_voice_call_ring_reject.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+            callEnd(false)
+        }
+
+        //来电接听
+        sky_voice_call_ring_pickup_audio.setOnClickListener {
+            acceptCall()
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_CALLING
+            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()
+        }
+        if (sipCore != null) {
+            sipCore!!.removeListener(coreListener)
+        }
+        RingPlayHelper.stopRingTone()
+
+        //如果当前在护理中则不操作门灯,如果不在护理中则重置门灯
+        if (("rk3128".equals(Build.MODEL) || "rk3368".equals(Build.MODEL) || "WDMK_I".equals(Build.MODEL))
+            && !Constant.inNursing) {
+            SerialPortHelper.setDoorLight(1, "111") //白色
+        }
+    }
+
+    private fun startOutgoing(): Boolean {
+        if (callState == 0) {
+            if (Constant.CALL_TYPE == Constant.VIDEO_CALL) {
+                VoiceUtil.startVideoCall(Constant.DEVICE_ID)
+            } else {
+                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()
+            return true
+        } else if (callState == 2) {
+            if (bedId != -1) {
+                if (Constant.CALL_TYPE == Constant.VIDEO_CALL) {
+                    VoiceUtil.startVideoCallBed(Constant.DEVICE_ID, bedId)
+                } else {
+                    VoiceUtil.startAudioCallBed(Constant.DEVICE_ID, bedId)
+                }
+                Constant.CALL_STATE = Constant.CALL_OUTGOING
+                sky_voice_call_timeout.visibility = View.VISIBLE
+                sky_voice_call_timer.visibility = View.GONE
+                startTimer()
+                return true
+            } else {
+                showMessage("bedId为空,呼叫失败")
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                return false
+            }
+        }
+
+        return false
+    }
+
+    //去电界面
+    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()
+
+        if (!audioCall) {
+            //显示视频画面
+            fullscreen_video_frame.visibility = View.VISIBLE
+            pip_video_frame.visibility = View.VISIBLE
+        }
+    }
+
+    //来电界面
+    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
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        callEnd(false)
+    }
+
+    private fun showCalling(audioOnly: Boolean) {
+        if (callEnded) {
+            return
+        }
+
+        if (audioOnly) {
+            ll_voice_call.visibility = View.VISIBLE
+        } else {
+            //显示视频画面
+            fullscreen_video_frame.visibility = View.VISIBLE
+            pip_video_frame.visibility = View.VISIBLE
+            ll_voice_call.visibility = View.GONE
+        }
+
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        sky_voice_call_calling_text.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()
+    }
+
+    //通话结束
+    override fun callEnd(handoff: Boolean) {
+        synchronized(this) {
+            RingPlayHelper.stopRingTone()
+            if (callEnded) {
+                return
+            }
+            callEnded = true
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            if (sky_voice_call_timer != null) {
+                sky_voice_call_timer.stop()
+            }
+
+            Log.e(TAG, "call end !!!!!!!!!!!!!!!!!!")
+
+            if (handoff) {
+                VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, Constant.interactionId)
+            }
+
+            backToMain()
+        }
+    }
+
+    private fun callTerminate() {
+        if (sipCore != null && sipCore!!.callsNb > 0) {
+            var call = sipCore!!.currentCall
+            if (call == null) {
+                call = sipCore!!.calls[0]
+            }
+            call!!.terminate()
+        }
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.type) {
+            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 (sipCore == null && TextUtils.isEmpty(curInteractionVO.toSipId)) {
+                                //通话失败,重置并返回主界面
+                                showMessage("Core或targetSipId为空!")
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                if (sky_voice_call_timer != null) {
+                                    sky_voice_call_timer.stop()
+                                }
+                                callEnd(true)
+                            } else {
+                                val addressToCall = sipCore!!.interpretUrl("3309")
+                                val params = sipCore!!.createCallParams(null)
+                                params.enableVideo(false)
+                                if (addressToCall != null) {
+                                    sipCore!!.inviteAddressWithParams(addressToCall, params)
+                                }
+                            }
+                        } 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()
+                            //janusClient!!.connect()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                            //我方呼出,对方不在线,设备离线或其它错误
+                            showMessage("呼叫失败,找不到设备或对方不在线!")
+                            AppTool.Time.delay(2000) {
+                                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()
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 247 - 0
callingdoor/src/main/java/com/wdkl/app/ncs/callingdoor/sip/WdklSipService.java

@@ -0,0 +1,247 @@
+package com.wdkl.app.ncs.callingdoor.sip;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+import android.widget.Toast;
+
+import com.wdkl.app.ncs.callingdoor.R;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+import org.linphone.core.Core;
+import org.linphone.core.CoreListenerStub;
+import org.linphone.core.Factory;
+import org.linphone.core.LogCollectionState;
+import org.linphone.core.ProxyConfig;
+import org.linphone.core.RegistrationState;
+import org.linphone.core.tools.Log;
+import org.linphone.mediastream.Version;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class WdklSipService extends Service {
+    private static final String START_SIPPHONE_LOGS = " ==== Device information dump ====";
+    //单例化服务,以便全局调用
+    private static WdklSipService sInstance;
+
+    private Handler mHandler;
+    private Timer mTimer;
+
+    private Core mCore;
+    private CoreListenerStub mCoreListener;
+
+    public static boolean isReady() {
+        return sInstance != null;
+    }
+
+    public static WdklSipService getInstance() {
+        return sInstance;
+    }
+
+    public static Core getCore() {
+        return sInstance.mCore;
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Toast.makeText(WdklSipService.this, "wdkl sip service", Toast.LENGTH_SHORT).show();
+
+        //首次调用必须使用 Factory相关方法
+        //这里开户调试日志及设置路径
+        String basePath = getFilesDir().getAbsolutePath();
+        Factory.instance().setLogCollectionPath(basePath);
+        Factory.instance().enableLogCollection(LogCollectionState.Enabled);
+        Factory.instance().setDebugMode(true, getString(R.string.app_name));
+
+        //收集一些设备信息
+        Log.i(START_SIPPHONE_LOGS);
+        dumpDeviceInformation();
+        dumpInstalledLinphoneInformation();
+
+        mHandler = new Handler();
+        //主监听器,根据事件调用界面
+        mCoreListener = new CoreListenerStub() {
+            /*@Override
+            public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
+                Toast.makeText(WdklSipService.this, message, Toast.LENGTH_SHORT).show();
+
+                if (state == Call.State.IncomingReceived) {
+                    Toast.makeText(WdklSipService.this, "Incoming call", Toast.LENGTH_LONG).show();
+                    //来电时将自动接听
+                    CallParams params = getCore().createCallParams(call);
+                    params.enableVideo(true);
+                    call.acceptWithParams(params);
+                } else if (state == Call.State.Connected) {
+                    //通话已建立完成,打开通话界面
+                    Intent intent = new Intent(WdklSipService.this, CallActivity.class);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(intent);
+                }
+            }*/
+
+            @Override
+            public void onRegistrationStateChanged(Core core, ProxyConfig cfg, RegistrationState state, String message) {
+                EventBus.getDefault().post(new MessageEvent(state, Constant.EVENT_SIP_REGISTER_STATUS));
+            }
+        };
+
+        try {
+            //复制一些源资源
+            //默认配置只能在首次时安装一次
+            copyIfNotExist(R.raw.linphonerc_default, basePath + "/.wdkl_sip_rc");
+            //用户配置,每次复制
+            copyFromPackage(R.raw.linphonerc_factory, "wdkl_sip_rc");
+        } catch (IOException ioe) {
+            Log.e(ioe);
+        }
+
+        //创建SIP核心并加载监听器
+        mCore = Factory.instance()
+                .createCore(basePath + "/.wdkl_sip_rc", basePath + "/wdkl_sip_rc", this);
+        mCore.addListener(mCoreListener);
+        //SIP核心配置完成
+        configureCore();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        super.onStartCommand(intent, flags, startId);
+        Toast.makeText(WdklSipService.this, "sip服务已启动", Toast.LENGTH_SHORT).show();
+
+        //如果服务已经在运行,则返回
+        if (sInstance != null) {
+            return START_STICKY;
+        }
+
+        //一旦服务启动,则一直保持
+        sInstance = this;
+
+        //SIP核心在创建和配置完成后,开启
+        mCore.start();
+        //必须定时运行 SIP核心 iterate()
+        TimerTask lTask = new TimerTask() {
+            @Override
+            public void run() {
+                mHandler.post(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mCore != null) {
+                                    mCore.iterate();
+                                }
+                            }
+                        });
+            }
+        };
+        mTimer = new Timer("wdkl sip scheduler");
+        mTimer.schedule(lTask, 0, 20);
+
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        mCore.removeListener(mCoreListener);
+        mTimer.cancel();
+        mCore.stop();
+        // A stopped Core can be started again
+        // To ensure resources are freed, we must ensure it will be garbage collected
+        mCore = null;
+        // Don't forget to free the singleton as well
+        sInstance = null;
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onTaskRemoved(Intent rootIntent) {
+        // For this sample we will kill the Service at the same time we kill the app
+        stopSelf();
+
+        super.onTaskRemoved(rootIntent);
+    }
+
+    private void configureCore() {
+        // We will create a directory for user signed certificates if needed
+        String basePath = getFilesDir().getAbsolutePath();
+        String userCerts = basePath + "/user-certs";
+        File f = new File(userCerts);
+        if (!f.exists()) {
+            if (!f.mkdir()) {
+                Log.e(userCerts + " can't be created.");
+            }
+        }
+        mCore.setUserCertificatesPath(userCerts);
+    }
+
+    private void dumpDeviceInformation() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DEVICE=").append(Build.DEVICE).append("\n");
+        sb.append("MODEL=").append(Build.MODEL).append("\n");
+        sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
+        sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
+        sb.append("Supported ABIs=");
+        for (String abi : Version.getCpuAbis()) {
+            sb.append(abi).append(", ");
+        }
+        sb.append("\n");
+        Log.i(sb.toString());
+    }
+
+    private void dumpInstalledLinphoneInformation() {
+        PackageInfo info = null;
+        try {
+            info = getPackageManager().getPackageInfo(getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            Log.e(nnfe);
+        }
+
+        if (info != null) {
+            Log.i(
+                    "[Service] sipphone version is ",
+                    info.versionName + " (" + info.versionCode + ")");
+        } else {
+            Log.i("[Service] sipphone version is unknown");
+        }
+    }
+
+    private void copyIfNotExist(int ressourceId, String target) throws IOException {
+        File lFileToCopy = new File(target);
+        if (!lFileToCopy.exists()) {
+            copyFromPackage(ressourceId, lFileToCopy.getName());
+        }
+    }
+
+    private void copyFromPackage(int ressourceId, String target) throws IOException {
+        FileOutputStream lOutputStream = openFileOutput(target, 0);
+        InputStream lInputStream = getResources().openRawResource(ressourceId);
+        int readByte;
+        byte[] buff = new byte[8048];
+        while ((readByte = lInputStream.read(buff)) != -1) {
+            lOutputStream.write(buff, 0, readByte);
+        }
+        lOutputStream.flush();
+        lOutputStream.close();
+        lInputStream.close();
+    }
+}

+ 20 - 0
callingdoor/src/main/res/raw/linphonerc_default

@@ -0,0 +1,20 @@
+[sip]
+contact="Linphone Android" <sip:allen@8.129.220.143>
+use_info=0
+use_ipv6=1
+keepalive_period=30000
+sip_port=-1
+sip_tcp_port=-1
+sip_tls_port=-1
+media_encryption=none
+
+[video]
+size=vga
+
+[app]
+tunnel=disabled
+push_notification=1
+
+[misc]
+max_calls=10
+history_max_size=100

+ 34 - 0
callingdoor/src/main/res/raw/linphonerc_factory

@@ -0,0 +1,34 @@
+
+#
+#This file shall not contain path referencing package name, in order to be portable when app is renamed.
+#Paths to resources must be set from LinphoneManager, after creating LinphoneCore.
+[net]
+mtu=1300
+#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
+download_bw=0
+upload_bw=0
+force_ice_disablement=0
+
+[sip]
+guess_hostname=1
+register_only_when_network_is_up=1
+auto_net_state_mon=1
+auto_answer_replacing_calls=1
+ping_with_options=0
+use_cpim=1
+
+[video]
+displaytype=MSAndroidTextureDisplay
+
+[misc]
+enable_basic_to_client_group_chat_room_migration=0
+enable_simple_group_chat_message_state=0
+aggregate_imdn=1
+notify_each_friend_individually_when_presence_received=0
+
+[app]
+activation_code_length=4
+prefer_basic_chat_room=1
+
+[assistant]
+xmlrpc_url=https://subscribe.linphone.org:444/wizard.php