Browse Source

腕表增加sip通话,修改toast提示,开启通话功能

weizhengliang 1 year ago
parent
commit
f24064f86e
27 changed files with 1814 additions and 251 deletions
  1. 3 1
      android_mobile/src/main/common/java/com/wdkl/ncs/host/service/WdklSipService.java
  2. 2 2
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt
  3. 13 0
      android_mobile/src/main/yd_watch_2/AndroidManifest.xml
  4. 8 30
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/NewCallListActivity.kt
  5. 9 1
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt
  6. 81 17
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt
  7. 2 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java
  8. 15 4
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt
  9. 9 1
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt
  10. 9 1
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt
  11. 26 23
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java
  12. 66 8
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt
  13. 30 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/BaseCallActivity.java
  14. 441 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/BaseSipCallFragment.java
  15. 27 62
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java
  16. 0 21
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java
  17. 414 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/FragmentSipAudio.java
  18. 1 73
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java
  19. 374 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/SipCallActivity.java
  20. 4 4
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/util/ActivityStackUtil.java
  21. 241 0
      android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/util/VoiceManagerUtil.java
  22. 22 0
      android_mobile/src/main/yd_watch_2/res/layout/user_setting_layout.xml
  23. 3 0
      app/src/main/code/com/wdkl/app/ncs/application/Application.kt
  24. 3 0
      common/build.gradle
  25. BIN
      common/libs/Toaster-12.6.aar
  26. 10 2
      common/src/main/code/com/wdkl/ncs/android/lib/utils/ExtendMethods.kt
  27. 1 1
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt

+ 3 - 1
android_mobile/src/main/common/java/com/wdkl/ncs/host/service/WdklSipService.java

@@ -20,6 +20,7 @@ import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
 import com.wdkl.ncs.android.component.home.util.ActivityStackUtil;
 import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.lib.utils.ExtendMethodsKt;
 import com.wdkl.ncs.android.lib.vo.MessageEvent;
 import com.wdkl.ncs.android.middleware.common.Constants;
 import com.wdkl.ncs.host.activity.CallActivity;
@@ -122,7 +123,8 @@ public class WdklSipService extends Service {
                     return;
                 }
 
-                Toast.makeText(WdklSipService.this, message, Toast.LENGTH_SHORT).show();
+                //Toast.makeText(WdklSipService.this, message, Toast.LENGTH_SHORT).show();
+                ExtendMethodsKt.showMessage(message);
                 Log.d("sipCall", ">>>>>>>>>>>> call state: " + state + ", " + call.getRemoteAddress().asString());
 
                 if (state == Call.State.IncomingReceived || state == Call.State.IncomingEarlyMedia) {

+ 2 - 2
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt

@@ -651,14 +651,14 @@ class WdKeepAliveService : AbsWorkService() {
                                 Thread.sleep(400)
                             }
 
-                            if (sosTcpModel.action == TcpAction.SOSAction.CALL || sosTcpModel.action == TcpAction.SOSAction.ROOM_CALL) {
+                            //if (sosTcpModel.action == TcpAction.SOSAction.CALL || sosTcpModel.action == TcpAction.SOSAction.ROOM_CALL) {
                                 val intent = Intent()
                                 intent.setClass(this, NewEventListActivity::class.java)
                                 //intent.putExtra("tcpModelStr", tcpModelIn.toJson())
                                 //intent.putExtra("newEvent", true)
                                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                                 startActivity(intent)
-                            }
+                            //}
                         }.start()
                     } else {
                         EventBus.getDefault().post(MessageEvent("update_event", Constants.EVENT_UPDATE_EVENT))

+ 13 - 0
android_mobile/src/main/yd_watch_2/AndroidManifest.xml

@@ -50,6 +50,19 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".ui.SipCallActivity"
+            android:showOnLockScreen="true"
+            android:showWhenLocked="true"
+            android:excludeFromRecents="true"
+            android:screenOrientation="portrait"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <service android:name=".service.FloatingService"/>
 
         <service android:name="com.wdkl.ncs.android.component.home.service.WdKeepAliveService">

+ 8 - 30
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/NewCallListActivity.kt

@@ -18,6 +18,7 @@ import com.wdkl.ncs.android.component.home.adapter.NewCallItemAdapter
 import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
 import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity
 import com.wdkl.ncs.android.component.home.util.RingPlayHelper
 import com.wdkl.ncs.android.component.home.util.SpeechUtil
 import com.wdkl.ncs.android.lib.base.BaseApplication
@@ -98,7 +99,12 @@ class NewCallListActivity : BaseToolActivity(), NewCallItemAdapter.CallClickList
                 Log.i(TAG, "来电:" + JSON.toJSONString(data))
 
                 //启动 activity
-                val intent = Intent(this, CallSingleActivity::class.java)
+                var intent: Intent
+                if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                    intent = Intent(activity, SipCallActivity::class.java)
+                } else {
+                    intent = Intent(activity, CallSingleActivity::class.java)
+                }
                 intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, roomId)
                 intent.putExtra(CallSingleActivity.EXTRA_MO, false)
                 intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
@@ -117,16 +123,6 @@ class NewCallListActivity : BaseToolActivity(), NewCallItemAdapter.CallClickList
                 val callTcp = VoiceUtil.voiceReject(data.tid, Constants.deviceId, interactionVO.fromDeviceId, interactionVO.id)
                 TcpClient.getInstance().sendMsg(callTcp.toJson())
 
-                /*NettyClient.instance.sendMsg(callTcp.toJson())
-                    .subscribe { success: Boolean ->
-                        if (success) {
-                            Log.d("CallItem", "TCP.发送消息完成")
-                        } else {
-                            Log.e("CallItem", "TCP.发送消息失败")
-                            HandleTcpConnect.instance.tcpReConnectWithMsgShow()
-                        }
-                    }*/
-
                 //移除该条呼叫
                 removeCall(interactionVO)
             }
@@ -160,17 +156,6 @@ class NewCallListActivity : BaseToolActivity(), NewCallItemAdapter.CallClickList
                         val voiceTransferTcpModel = VoiceUtil.voiceTransfer(item.tid, Constants.deviceId, interactionVO.fromDeviceId, interactionVO.id);
                         TcpClient.getInstance().sendMsg(voiceTransferTcpModel.toJson())
 
-                        /*val voiceTransferTcpModel = VoiceUtil.voiceTransferToHost(Constants.deviceId, interactionVO.fromDeviceId, interactionVO)
-                        NettyClient.instance.sendMsg(voiceTransferTcpModel.toJson())
-                            .subscribe {
-                                if (it) {
-                                    //Log.d(TAG, "TCP.发送消息完成")
-                                } else {
-                                    //Log.e(TAG, "TCP.发送消息失败")
-                                    HandleTcpConnect.instance.tcpReConnectWithMsgShow()
-                                }
-                            }*/
-
                         removeCall = true
                         break
                     }
@@ -262,13 +247,6 @@ class NewCallListActivity : BaseToolActivity(), NewCallItemAdapter.CallClickList
     fun onMoonEvent(messageEvent: MessageEvent) {
         if (messageEvent.tag == Constants.EVENT_UPDATE_CALL) { //新呼叫
             updateCallList()
-        } /*else if (messageEvent.tag == Constants.EVENT_NEW_TCP) {
-            val tcpModel = messageEvent.getMessage() as TcpModel
-            if (tcpModel.getAction() === TcpAction.VoiceAction.CANCEL || tcpModel.action == TcpAction.VoiceAction.VOICE_OFF) {
-                //对方取消呼叫或者呼叫已被其他主机处理(接听或拒绝)
-                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
-                removeCall(interactionVO)
-            }
-        }*/
+        }
     }
 }

+ 9 - 1
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt

@@ -16,7 +16,10 @@ import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.adapter.WatchCallRecordsItemAdapter
 import com.wdkl.ncs.android.component.home.databinding.WatchActivityCallRecordsBinding
 import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity
+import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.middleware.common.Constants
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.middleware.logic.contract.home.WatchCallRecordsFragmentContract
@@ -184,7 +187,12 @@ class WatchCallRecordsActivity : BaseActivity<WatchCallRecordsFragmentPresenter,
                     } else {
                         val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, receivedData!!.deviceId)
                         //启动通话界面
-                        val intent = Intent(this@WatchCallRecordsActivity, CallSingleActivity::class.java)
+                        var intent: Intent
+                        if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                            intent = Intent(this@WatchCallRecordsActivity, SipCallActivity::class.java)
+                        } else {
+                            intent = Intent(this@WatchCallRecordsActivity, CallSingleActivity::class.java)
+                        }
                         intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
                         intent.putExtra(CallSingleActivity.EXTRA_MO, true)
                         intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)

+ 81 - 17
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt

@@ -51,6 +51,7 @@ import com.wdkl.ncs.android.middleware.tcp.TcpClientHandler
 import com.wdkl.ncs.android.middleware.tcp.enums.RoleTypeEnum
 import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import com.wdkl.ncs.android.middleware.utils.ContactHelper
+import com.wdkl.ncs.host.service.WdklSipService
 import com.wdkl.ncs.keepbackground.utils.SpManager
 import com.wdkl.ncs.keepbackground.work.DaemonEnv
 import com.wdkl.rtc.util.JanusConstant
@@ -59,6 +60,8 @@ import kotlinx.android.synthetic.main.watch_activity_home2.*
 import kotlinx.android.synthetic.main.watch_activity_register.*
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
+import org.linphone.core.AccountCreator
+import org.linphone.core.TransportType
 import java.util.*
 import kotlin.collections.ArrayList
 
@@ -82,6 +85,8 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
     private lateinit var netWorkChangeReceiver: NetWorkChangeReceiver
 
+    private var mAccountCreator: AccountCreator? = null
+
     override fun onCreate(savedInstanceState: Bundle?) {
         if (!isTaskRoot) {
             finish()
@@ -110,6 +115,17 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
         initCountDownTimer()
 
+        if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+            //启动sip服务
+            val serviceIntent = Intent(BaseApplication.appContext, WdklSipService::class.java)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                //android8.0以上通过startForegroundService启动service
+                startForegroundService(serviceIntent)
+            } else {
+                startService(serviceIntent)
+            }
+        }
+
         //网络强度监听
         teleManager = (applicationContext.getSystemService(Context.TELEPHONY_SERVICE)) as TelephonyManager
         netType = NetHelper.getInstance().getNetworkState(applicationContext)
@@ -223,23 +239,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
      * 返回的tcp信息
      */
     override fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO) {
-        /*Log.d(TAG, "获取服务器IP完成")
-        if (tcpSeverDTO.publicIp != null && tcpSeverDTO.tcpPort != null && tcpSeverDTO.readerIdleTime != null) {
-            Constants.tcpServer = tcpSeverDTO.publicIp
-            Constants.tcpPort = tcpSeverDTO.tcpPort
-            Constants.heartBeat = tcpSeverDTO.readerIdleTime
-            tv_server_ip.text = tcpSeverDTO.publicIp
-
-            //成功获取到数据后再启动keepService并连接tcp服务器
-            if (DaemonEnv.app == null && !Strings.isNullOrEmpty(Constants.tcpServer) && !WdKeepAliveService.instanceCreated) {
-                Log.d(TAG, "开始 WdKeepAliveService")
-                //保活守护进程
-                DaemonEnv.init(this)
-                DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
-            }
 
-            presenter.getDeviceVO(Constants.imei)
-        }*/
     }
 
     override fun setServerInfo(serverIpInfo: ServerIpInfo) {
@@ -261,6 +261,36 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
                 DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
             }*/
 
+            if (serverIpInfo.sipPublicIp != null) {
+                Constants.sip_ip = serverIpInfo.sipPublicIp
+            }
+            if (serverIpInfo.sipPublicPort != null) {
+                Constants.sip_port = serverIpInfo.sipPublicPort
+            }
+
+            if (serverIpInfo.voiceType != null) {
+                val orgSipEnable = SettingConfig.getSipEnabled(activity)
+                if ("sip".equals(serverIpInfo.voiceType)) {
+                    //使用sip通话
+                    Constants.voice_type = "sip"
+                    SettingConfig.setSipEnable(activity, true)
+                    if (!orgSipEnable) {
+                        AppTool.Time.delay(8000) {
+                            AppUtils.restartApp()
+                        }
+                    }
+                } else {
+                    //使用webrtc通话
+                    Constants.voice_type = "webrtc"
+                    SettingConfig.setSipEnable(activity, false)
+                    if (orgSipEnable) {
+                        AppTool.Time.delay(8000) {
+                            AppUtils.restartApp()
+                        }
+                    }
+                }
+            }
+
             presenter.getDeviceVO(Constants.imei)
         }
 
@@ -387,9 +417,11 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
             return
         }
 
+        if (data.sipId != null) {
+            Constants.sipId = data.sipId
+        }
         Constants.partId = data.partId
         Constants.deviceId = data.id
-        Constants.sipId = data.sipId
         Constants.deviceType = data.deviceType
 
         if (DaemonEnv.app == null && !WdKeepAliveService.instanceCreated) {
@@ -533,6 +565,38 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         if (partSettingDO.communicationModeNurse != null) {
             Constants.nursePhoneType = partSettingDO.communicationModeNurse
         }
+
+        //配置sip账号并连接sip服务器
+        if (SettingConfig.getSipEnabled(activity)) {
+            //配置sip账户
+            if (WdklSipService.getCore() != null) {
+                mAccountCreator = WdklSipService.getCore().createAccountCreator(null)
+                // 以下三项必须
+                if (!TextUtils.isEmpty(Constants.sipId) && !TextUtils.isEmpty(Constants.sip_ip)) {
+                    Log.e(TAG, "sip connect: ${Constants.sipId}@${Constants.sip_ip}:${Constants.sip_port}")
+                    mAccountCreator!!.setDomain(Constants.sip_ip)
+                    mAccountCreator!!.setUsername(Constants.sipId)
+                    mAccountCreator!!.setPassword(Constants.sipId)
+                    //默认使用udp
+                    mAccountCreator!!.transport = TransportType.Udp
+
+                    // 这里会自动创建代理配置、认证信息到 SIP核心
+                    val cfg = mAccountCreator!!.createProxyConfig()
+                    // 确保新创建的是最新
+                    WdklSipService.getCore().defaultProxyConfig = cfg
+
+                    /*if (Constants.sip_port != null) {
+                        var transports = WdklSipService.getCore().transports
+                        transports.udpPort = Constants.sip_port!!
+                        transports.tcpPort = Constants.sip_port!!
+                        transports.tlsPort = -1
+                        WdklSipService.getCore().transports = transports
+                    }*/
+                } else {
+                    showMessage("SIP empty")
+                }
+            }
+        }
     }
 
     fun onTcpConnectSuccess() {

+ 2 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java

@@ -72,6 +72,8 @@ public class WatchUserSettingActivity extends Activity {
         tvDeviceIp.setText(NetHelper.getInstance().getLocalIP());
         tvServerIp.setText(Constants.Companion.getTcpServer());
         tvPhoneType.setText("手机-" + Constants.Companion.getPhoneType() + ", 分机-" + Constants.Companion.getBedPhoneType() + ", 主机-" + Constants.Companion.getNursePhoneType());
+        TextView tvVoiceMode = findViewById(R.id.tv_voice_type);
+        tvVoiceMode.setText(Constants.Companion.getVoice_type());
 
         if (SettingConfig.getTransCall(BaseApplication.appContext) == 1) {
             callYes.setChecked(true);

+ 15 - 4
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt

@@ -13,12 +13,15 @@ import com.enation.javashop.utils.base.widget.LoadingDialog
 import com.google.gson.Gson
 import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.databinding.EventListItemBinding
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity
 import com.wdkl.ncs.android.component.home.util.ImPlayDialogHelper
 import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
 import com.wdkl.ncs.android.middleware.common.Constants
 import com.wdkl.ncs.android.component.home.util.TimeTransition
 import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.middleware.api.ApiManager
@@ -267,9 +270,13 @@ class NewEventItemAdapter(var data:ArrayList<InteractionVO>, val activity: Activ
             binding.eliCallout.setOnClickListener {
                 if (itemData.fromDeviceMemberId == Constants.memberId){
                     DeviceChannel.calling = true
-
                     val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.toDeviceId)
-                    val intent = Intent(activity, CallSingleActivity::class.java)
+                    var intent: Intent
+                    if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                        intent = Intent(activity, SipCallActivity::class.java)
+                    } else {
+                        intent = Intent(activity, CallSingleActivity::class.java)
+                    }
                     intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
                     intent.putExtra(CallSingleActivity.EXTRA_MO, true)
                     intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
@@ -281,8 +288,12 @@ class NewEventItemAdapter(var data:ArrayList<InteractionVO>, val activity: Activ
                 } else {
                     DeviceChannel.calling = true
                     val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.fromDeviceId)
-
-                    val intent = Intent(activity, CallSingleActivity::class.java)
+                    var intent: Intent
+                    if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                        intent = Intent(activity, SipCallActivity::class.java)
+                    } else {
+                        intent = Intent(activity, CallSingleActivity::class.java)
+                    }
                     intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
                     intent.putExtra(CallSingleActivity.EXTRA_MO, true)
                     intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)

+ 9 - 1
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt

@@ -15,9 +15,12 @@ import com.google.common.base.Strings
 import com.google.gson.JsonObject
 import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.databinding.TakeoverItemBinding
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity
 import com.wdkl.ncs.android.middleware.common.Constants
 import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.middleware.tcp.TcpClient
@@ -156,7 +159,12 @@ class TakeoverItemAdapter(var data:ArrayList<JsonObject>, val activity: Activity
                     } else {
                         //网络电话
                         val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.get("id").asInt)
-                        val intent = Intent(activity, CallSingleActivity::class.java)
+                        var intent: Intent
+                        if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                            intent = Intent(activity, SipCallActivity::class.java)
+                        } else {
+                            intent = Intent(activity, CallSingleActivity::class.java)
+                        }
                         intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
                         intent.putExtra(CallSingleActivity.EXTRA_MO, true)
                         intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)

+ 9 - 1
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt

@@ -14,9 +14,12 @@ import com.enation.javashop.utils.base.tool.CommonTool
 import com.enation.javashop.utils.base.widget.LoadingDialog
 import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.databinding.AdapterWatchContactsItemBinding
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity
 import com.wdkl.ncs.android.middleware.common.Constants
 import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
@@ -124,7 +127,12 @@ class WatchContactsItemAdapter(val data:ArrayList<WatchContactsVO>, val context:
                 } else {
                     //通话
                     val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.deviceId)
-                    val intent = Intent(context, CallSingleActivity::class.java)
+                    var intent: Intent
+                    if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                        intent = Intent(context, SipCallActivity::class.java)
+                    } else {
+                        intent = Intent(context, CallSingleActivity::class.java)
+                    }
                     intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
                     intent.putExtra(CallSingleActivity.EXTRA_MO, true)
                     intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)

+ 26 - 23
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java

@@ -19,10 +19,15 @@ import androidx.annotation.Nullable;
 
 import com.google.gson.Gson;
 import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.ui.BaseCallActivity;
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity;
+import com.wdkl.ncs.android.component.home.ui.SipCallActivity;
 import com.wdkl.ncs.android.component.home.util.ActivityStackUtil;
 import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
 import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
 import com.wdkl.ncs.android.middleware.common.Constants;
 import com.wdkl.ncs.android.lib.vo.MessageEvent;
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
@@ -136,7 +141,12 @@ public class FloatingService extends Service {
     }
 
     private void resumeCallActivity() {
-        Intent intent = new Intent(FloatingService.this, CallSingleActivity.class);
+        Intent intent;
+        if (SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+            intent = new Intent(FloatingService.this, SipCallActivity.class);
+        } else {
+            intent = new Intent(FloatingService.this, CallSingleActivity.class);
+        }
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent);
     }
@@ -185,6 +195,12 @@ public class FloatingService extends Service {
         }
     }
 
+    private void finishCallActivity() {
+        BaseCallActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+        if (callSingleActivity != null) {
+            callSingleActivity.finish();
+        }
+    }
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onEvent(MessageEvent messageEvent) {
@@ -208,44 +224,31 @@ public class FloatingService extends Service {
                 }
             } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) { //对方挂断
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
-                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
-                    if (callSingleActivity != null) {
-                        callSingleActivity.finish();
-                    }
+                    finishCallActivity();
                 }
             } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
-                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
-                    if (callSingleActivity != null) {
-                        callSingleActivity.finish();
-                    }
+                    finishCallActivity();
                 }
             } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
-                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
-                    if (callSingleActivity != null) {
-                        callSingleActivity.finish();
-                    }
+                    finishCallActivity();
                 }
             } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
-                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
-                    if (callSingleActivity != null) {
-                        callSingleActivity.finish();
-                    }
+                    finishCallActivity();
                 }
-            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL || tcpModel.getAction() == TcpAction.VoiceAction.VOICE_OFF) {
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
-                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
-                    if (callSingleActivity != null) {
-                        callSingleActivity.finish();
-                    }
+                    RingPlayHelper.stopRingTone();
+                    SpeechUtil.getInstance().stopSpeak();
+                    finishCallActivity();
                 }
             }
         } else if (tag == 1) {
             TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
             if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS){ //服务器返回成功
-                CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                BaseCallActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
                 if (callSingleActivity != null) {
                     callSingleActivity.callOutSuccess(tcpModel);
                 }

+ 66 - 8
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt

@@ -539,8 +539,9 @@ class WdKeepAliveService : AbsWorkService() {
             999 -> {
                 Util.wakeUpAndUnlock()
                 val sosTcpModel = messageEvent.getMessage() as TcpModel
-                val sosInteractionVO = Gson().fromJson(sosTcpModel.data.toString(), InteractionVO::class.java)
-                if (sosTcpModel.action === TcpAction.SOSAction.CANCEL) {
+                if (sosTcpModel.action == TcpAction.SOSAction.CANCELED) {
+                    //紧急呼叫处理完成
+                    //if (mNewEventListActive) {
                     //先停止铃声或其他音频播放
                     SpeechUtil.getInstance().stopSpeak()
                     MediaPlayHelper.getInstance().stopMusic(true)
@@ -549,12 +550,35 @@ class WdKeepAliveService : AbsWorkService() {
                     if (mVibrator != null) {
                         mVibrator.cancel()
                     }
+                    //}
 
-                    if (!Strings.isNullOrEmpty(sosInteractionVO.toRoleName)) {
+                    Log.e(TAG, sosTcpModel.data.toString())
+                    val interactionId =  sosTcpModel.data.toString().toInt()
+                    val iterator = Constants.eventList.iterator()
+                    while (iterator.hasNext()) {
+                        val it = iterator.next()
+                        if (it.id == interactionId) {
+                            iterator.remove()
+                        }
+                    }
+                    EventBus.getDefault().post(MessageEvent("update_event", Constants.EVENT_UPDATE_EVENT))
+
+                } else if (sosTcpModel.action === TcpAction.SOSAction.CANCEL) {
+                    val sosInteractionVO = Gson().fromJson(sosTcpModel.data.toString(), InteractionVO::class.java)
+                    //先停止铃声或其他音频播放
+                    SpeechUtil.getInstance().stopSpeak()
+                    MediaPlayHelper.getInstance().stopMusic(true)
+                    RingPlayHelper.stopRingTone()
+
+                    if (mVibrator != null) {
+                        mVibrator.cancel()
+                    }
+
+                    /*if (!Strings.isNullOrEmpty(sosInteractionVO.toRoleName)) {
                         showMessage(sosInteractionVO.toRoleName + " " + sosInteractionVO.toMemberName + " 已响应")
                     } else {
                         showMessage("已响应")
-                    }
+                    }*/
 
                     var iterator = Constants.eventList.iterator()
                     while (iterator.hasNext()) {
@@ -565,7 +589,8 @@ class WdKeepAliveService : AbsWorkService() {
                     }
                     EventBus.getDefault().post(MessageEvent(sosTcpModel, Constants.EVENT_UPDATE_EVENT))
 
-                } else if (sosTcpModel.action === TcpAction.SOSAction.CALL) {
+                } else if (sosTcpModel.action === TcpAction.SOSAction.CALL || sosTcpModel.action == TcpAction.SOSAction.ROOM_CALL) {
+                    val sosInteractionVO = Gson().fromJson(sosTcpModel.data.toString(), InteractionVO::class.java)
                     //取消掉语音留言录音
                     RecordHelper.getInstance().stopCancelRecordByOther(true)
 
@@ -592,7 +617,7 @@ class WdKeepAliveService : AbsWorkService() {
                                 Thread.sleep(400)
                             }
 
-                            if (sosTcpModel.action == TcpAction.SOSAction.CALL) {
+                            if (sosTcpModel.action == TcpAction.SOSAction.CALL || sosTcpModel.action == TcpAction.SOSAction.ROOM_CALL) {
                                 val intent = Intent()
                                 intent.setClass(this, NewEventListActivity::class.java)
                                 //intent.putExtra("tcpModelStr", tcpModelIn.toJson())
@@ -604,6 +629,39 @@ class WdKeepAliveService : AbsWorkService() {
                     } else {
                         EventBus.getDefault().post(MessageEvent(sosTcpModel, Constants.EVENT_UPDATE_EVENT))
                     }
+                } else {
+                    val sosInteractionVO = Gson().fromJson(sosTcpModel.data.toString(), InteractionVO::class.java)
+                    //取消掉语音留言录音
+                    RecordHelper.getInstance().stopCancelRecordByOther(true)
+
+                    if (Constants.oldEvent) {
+                        Constants.eventList.clear()
+                    }
+                    Constants.oldEvent = false
+                    Constants.eventList.add(sosInteractionVO)
+                    //语音播报处理
+                    eventSpeechOut(sosTcpModel)
+                    //震动提醒
+                    handleVibrator()
+
+                    if (!mNewEventListActive) {
+                        Thread {
+                            while (DeviceChannel.calling) {
+                                Thread.sleep(400)
+                            }
+
+                            //if (sosTcpModel.action == TcpAction.SOSAction.CALL || sosTcpModel.action == TcpAction.SOSAction.ROOM_CALL) {
+                                val intent = Intent()
+                                intent.setClass(this, NewEventListActivity::class.java)
+                                //intent.putExtra("tcpModelStr", tcpModelIn.toJson())
+                                //intent.putExtra("newEvent", true)
+                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                                startActivity(intent)
+                            //}
+                        }.start()
+                    } else {
+                        EventBus.getDefault().post(MessageEvent("update_event", Constants.EVENT_UPDATE_EVENT))
+                    }
                 }
             }
 
@@ -744,7 +802,7 @@ class WdKeepAliveService : AbsWorkService() {
 
 
         //播报语音提醒
-        val interactionVO = Gson().fromJson<InteractionVO>(model.data.toString(), InteractionVO::class.java)
+        val interactionVO = Gson().fromJson(model.data.toString(), InteractionVO::class.java)
         if (model.type == TcpType.EVENT && model.action == TcpAction.EventAction.KEY_CLICK) {
             val eventStr = Util.appendSpace(interactionVO.fromFrameFullName.replace("-", ",")) + ", " + interactionVO.data
 
@@ -754,7 +812,7 @@ class WdKeepAliveService : AbsWorkService() {
                 //MediaPlayHelper.getInstance().playResMusic(R.raw.new_event, 1.0f, false)
                 RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.new_event, false)
             }
-        } else if (model.type == TcpType.SOS && model.action == TcpAction.SOSAction.CALL) {
+        } else if (model.type == TcpType.SOS) {
             AppTool.Time.delay(500) {
                 //MediaPlayHelper.getInstance().playResMusic(R.raw.sos, 1.0f, true)
                 RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.sos, true)

+ 30 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/BaseCallActivity.java

@@ -0,0 +1,30 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.os.Vibrator;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.enation.javashop.utils.base.widget.LoadingDialog;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+
+public abstract class BaseCallActivity extends AppCompatActivity {
+
+    public static final String EXTRA_ROOM_ID = "roomId";
+    public static final String EXTRA_MO = "isOutGoing";
+    public static final String EXTRA_AUDIO_ONLY = "audioOnly";
+    public static final String EXTRA_TCPMODEL = "tcpModel";
+    public static final String EXTRA_SHOWNAME = "showName";
+
+    TcpModel recTcpModel;
+    String showName;
+    LoadingDialog loadingDialog;
+    boolean callConnected = false;
+    //振动
+    Vibrator mVibrator;
+
+    abstract public void callOutSuccess(TcpModel tcpModel);
+
+    abstract void sendHandOffTcp();
+
+    abstract boolean isOutgoing();
+}

+ 441 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/BaseSipCallFragment.java

@@ -0,0 +1,441 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Chronometer;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.component.home.util.VoiceManagerUtil;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.lib.utils.ExtendMethodsKt;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.host.service.WdklSipService;
+import com.wdkl.ncs.host.util.AudioRouteUtils;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+import org.linphone.core.Address;
+import org.linphone.core.Call;
+import org.linphone.core.CallParams;
+import org.linphone.core.Core;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public abstract class BaseSipCallFragment extends Fragment {
+    private static final String TAG = "BaseSipCallFragment";
+    ImageView minimizeImageView;
+    ImageView portraitImageView;// 用户头像
+    TextView nameTextView; // 用户昵称
+    TextView descTextView;  // 状态提示用语
+    Chronometer durationTextView; // 通话时长
+
+    LinearLayout transLinearLayout;
+    LinearLayout hangupLinearLayout;
+    LinearLayout muteLinearLayout;
+    LinearLayout speakerLinearLayout;
+
+    ImageView outgoingHangupImageView;
+    ImageView incomingHangupImageView;
+    ImageView incomingTransImageView;
+    ImageView acceptImageView;
+    TextView tvStatus;
+    View outgoingActionContainer;
+    View incomingActionContainer;
+    View connectedActionContainer;
+
+    View lytParent;
+
+    boolean isOutgoing = true;
+
+    BaseCallActivity callActivity;
+    CallHandler handler;
+    boolean isHandoff = false;
+
+    protected Core core;
+
+    public static final int WHAT_DELAY_END_CALL = 0x01;
+    public static final int FINISH_CURRENT = 0x02;
+    public static final int WHAT_DELAY_CHECK_CALL = 0x03;
+
+    HeadsetPlugReceiver headsetPlugReceiver;
+
+    private boolean callSuccess = false;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+
+        handler = new CallHandler();
+        EventBus.getDefault().register(this);
+
+        core = WdklSipService.getCore();
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(getLayout(), container, false);
+        initView(view);
+        init();
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        if (isOutgoing) {
+            RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.ring_back2, true);
+        } else {
+            RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.ring_tone, true);
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        if (handler != null) {
+            handler.removeCallbacksAndMessages(null);
+        }
+        if (durationTextView != null)
+            durationTextView.stop();
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        EventBus.getDefault().unregister(this);
+    }
+
+    abstract int getLayout();
+    abstract void showCallConnected();
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        callActivity = (SipCallActivity) getActivity();
+        if (callActivity != null) {
+            isOutgoing = callActivity.isOutgoing();
+            headsetPlugReceiver = new HeadsetPlugReceiver();
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_HEADSET_PLUG);
+            callActivity.registerReceiver(headsetPlugReceiver, filter);
+        }
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        callActivity.unregisterReceiver(headsetPlugReceiver);  //注销监听
+        callActivity = null;
+    }
+
+    public void initView(View view) {
+        lytParent = view.findViewById(R.id.lytParent);
+        minimizeImageView = view.findViewById(R.id.minimizeImageView);
+        portraitImageView = view.findViewById(R.id.portraitImageView);
+        nameTextView = view.findViewById(R.id.nameTextView);
+        descTextView = view.findViewById(R.id.descTextView);
+        durationTextView = view.findViewById(R.id.durationTextView);
+        outgoingHangupImageView = view.findViewById(R.id.outgoingHangupImageView);
+        incomingHangupImageView = view.findViewById(R.id.incomingHangupImageView);
+        incomingTransImageView = view.findViewById(R.id.incomingTransImageView);
+        acceptImageView = view.findViewById(R.id.acceptImageView);
+        tvStatus = view.findViewById(R.id.tvStatus);
+        outgoingActionContainer = view.findViewById(R.id.outgoingActionContainer);
+        incomingActionContainer = view.findViewById(R.id.incomingActionContainer);
+        connectedActionContainer = view.findViewById(R.id.connectedActionContainer);
+        transLinearLayout = view.findViewById(R.id.transLinearLayout);
+        hangupLinearLayout = view.findViewById(R.id.hangupLinearLayout);
+        muteLinearLayout = view.findViewById(R.id.muteLinearLayout);
+        speakerLinearLayout = view.findViewById(R.id.speakerLinearLayout);
+
+        durationTextView.setVisibility(View.GONE);
+        if (isOutgoing) {
+            handler.sendEmptyMessageDelayed(WHAT_DELAY_CHECK_CALL, 5000); //5s后检查是否呼叫成功
+        }
+        handler.sendEmptyMessageDelayed(WHAT_DELAY_END_CALL, 60 * 1000);//60s之后未接通,则挂断电话
+    }
+
+    public void init() {
+        if (callActivity.recTcpModel.getData()!=null){
+            InteractionVO interactionVO = new Gson().fromJson(callActivity.recTcpModel.getData().toString(), InteractionVO.class);
+            Constants.Companion.setInteractionId(interactionVO.getId());
+        }
+
+        if (isOutgoing) {
+            startOutCall();
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    private void startOutCall() {
+        //发送tcp call
+        TcpCallback transaction = new TcpCallback(callActivity.recTcpModel.getTid()) {
+            @Override
+            public void onSuccess(JSONObject jsonObject) {
+                //
+            }
+
+            @Override
+            public void onFailed(JSONObject jsonObject) {
+                if (callActivity != null) {
+                    callActivity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            ExtendMethodsKt.showMessage("Failed");
+                        }
+                    });
+                    callActivity.finish();
+                }
+            }
+        };
+        TcpClient.getInstance().sendTcp(callActivity.recTcpModel, false, transaction);
+    }
+
+    public void callOutSuccess(TcpModel tcpModel) {
+        callActivity.recTcpModel = tcpModel;
+        InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
+        Constants.Companion.setInteractionId(interactionVO.getId());
+        callSuccess = true;
+        //callSingleActivity.janusClient.connect();
+        Log.d(TAG,"voice#success =>" + callActivity.recTcpModel.toJson());
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.call_success, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    public void startRefreshTime() {
+        if (durationTextView != null) {
+            leftTime = 0;
+            durationTextView.setOnChronometerTickListener(null);
+            durationTextView.stop();
+            durationTextView.setVisibility(View.VISIBLE);
+            durationTextView.setBase(SystemClock.elapsedRealtime());
+            durationTextView.start();
+        }
+    }
+
+    private long leftTime = 60;
+    public void countDownTime() {
+        if (durationTextView != null) {
+            durationTextView.setVisibility(View.VISIBLE);
+            durationTextView.setBase(SystemClock.elapsedRealtime());
+            durationTextView.start();
+            durationTextView.setOnChronometerTickListener(
+                    new Chronometer.OnChronometerTickListener() {
+                        @Override
+                        public void onChronometerTick(Chronometer chronometer) {
+                            leftTime--;
+                            Date d = new Date(leftTime * 1000);
+                            SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss");
+                            chronometer.setText(timeFormat.format(d));
+                            if (leftTime == 0) {
+                                durationTextView.stop();
+                            }
+                        }
+                    });
+        }
+    }
+
+    void runOnUiThread(Runnable runnable) {
+        if (callActivity != null) {
+            callActivity.runOnUiThread(runnable);
+        }
+    }
+
+    @SuppressLint("HandlerLeak")
+    class CallHandler extends Handler {
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                case WHAT_DELAY_END_CALL:
+                    if (core.getCurrentCall() != null) {
+                        Call.State state = core.getCurrentCall().getState();
+                        if (callActivity != null && state != Call.State.Connected && !isHandoff) {
+                            callActivity.sendHandOffTcp();
+                            callActivity.finish();
+                        }
+                    }
+                    break;
+                case WHAT_DELAY_CHECK_CALL:
+                    if (callActivity != null && !callSuccess) {
+                        Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.net_error, Toast.LENGTH_SHORT).show();
+                    }
+                    break;
+                case FINISH_CURRENT:
+                    if (callActivity != null) {
+                        callActivity.finish();
+                    }
+                    break;
+            }
+        }
+    }
+
+    public abstract void onBackPressed();
+
+    public abstract boolean onKeyUp(int keyCode, KeyEvent event);
+
+    protected abstract void handleHeadsetHook();
+
+    class HeadsetPlugReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.hasExtra("state")) {
+                if (intent.getIntExtra("state", 0) == 0) { //拔出耳机
+                    AudioRouteUtils.Companion.routeAudioToSpeaker(core, null, false);
+                } else if (intent.getIntExtra("state", 0) == 1) { //插入耳机
+                    AudioRouteUtils.Companion.routeAudioToHeadset(core, null, false);
+                }
+            }
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onEvent(MessageEvent messageEvent) {
+        int code = messageEvent.getTag();
+        //TCP处理
+        if (code == 2) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            Log.i(TAG, "收到数据222: " + tcpModel.toJson());
+            int curInteractionId = -1;
+            InteractionVO interactionVO = null;
+            if (tcpModel.getData() != null) {
+                interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
+                curInteractionId = interactionVO.getId();
+            }
+
+            if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    MediaPlayHelper.getInstance().stopMusic(true);
+                    RingPlayHelper.stopRingTone();
+                    SpeechUtil.getInstance().stopSpeak();
+                    callActivity.recTcpModel = tcpModel;
+                    Log.d(TAG, "voice#accept =>" + callActivity.recTcpModel.toJson());
+                    Log.i(TAG, "对方接听电话啦");
+
+                    if (core == null || interactionVO == null || TextUtils.isEmpty(interactionVO.getToSipId())) {
+                        //通话失败,重置并返回主界面
+                        Toast.makeText(BaseSipCallFragment.this.getContext(), "sip_core targetSipId empty!", Toast.LENGTH_SHORT).show();
+                        handler.sendEmptyMessageDelayed(FINISH_CURRENT, 1000);
+                    } else {
+                        Address addressToCall = core.interpretUrl(interactionVO.getToSipId());
+                        CallParams params = core.createCallParams(null);
+                        params.setVideoEnabled(false);
+                        if (addressToCall != null) {
+                            core.inviteAddressWithParams(addressToCall, params);
+                            Log.d(TAG, ">>>>>>>>>>> invite address: " + addressToCall.asString());
+                        }
+                    }
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) { //对方挂断
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    isHandoff = true;
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.call_end, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.call_reject, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.call_busy, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                //if (curInteractionId == interactionId) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 1000);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.call_failed, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                //}
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL || tcpModel.getAction() == TcpAction.VoiceAction.VOICE_OFF) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(BaseSipCallFragment.this.getContext(), R.string.str_cancel, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            }
+        } else if (code == 1) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            Log.i(TAG, "收到数据111: " + tcpModel.toJson());
+            if (isOutgoing && tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS){ //服务器返回成功
+                callOutSuccess(tcpModel);
+            }
+        } else if (code == Constants.EVENT_HEADSET_HOOK) {
+            //收到耳机按键
+            handleHeadsetHook();
+        } else if (code == Constants.EVENT_END_CALL) {
+            handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+        } else if (code == Constants.SIP_CONNECTED) {
+            if (core != null) {
+                core.setMicEnabled(true);
+            }
+            showCallConnected();
+        }
+    }
+}

+ 27 - 62
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java

@@ -21,7 +21,6 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Toast;
 
-import androidx.appcompat.app.AppCompatActivity;
 import androidx.fragment.app.FragmentManager;
 
 import com.alibaba.fastjson.JSONObject;
@@ -61,13 +60,8 @@ import pub.devrel.easypermissions.EasyPermissions;
 /**
  * 单人通话界面
  */
-public class CallSingleActivity extends AppCompatActivity {
+public class CallSingleActivity extends BaseCallActivity {
 
-    public static final String EXTRA_ROOM_ID = "roomId";
-    public static final String EXTRA_MO = "isOutGoing";
-    public static final String EXTRA_AUDIO_ONLY = "audioOnly";
-    public static final String EXTRA_TCPMODEL = "tcpModel";
-    public static final String EXTRA_SHOWNAME = "showName";
     public static final String EXTRA_RING = "ring";
     private static final String TAG = "CallSingleActivity";
 
@@ -78,8 +72,6 @@ public class CallSingleActivity extends AppCompatActivity {
     //private BigInteger localUserId = BigInteger.valueOf(Integer.parseInt(Constants.Companion.getSipId()));
 
     boolean isAudioOnly = true;
-    String showName;
-    TcpModel recTcpModel;
     JanusClient janusClient;
     ILogUpload iLogUpload = new LogUpload();
     VideoRoomCallback videoRoomCallback;
@@ -87,15 +79,10 @@ public class CallSingleActivity extends AppCompatActivity {
     boolean ringUp = true;
 
     private SingleCallFragment currentFragment;
-    //振动
-    Vibrator mVibrator;
-    public LoadingDialog loadingDialog;
 
     private boolean mServiceBound = false;
     private HomeWatcher mHomeWatcher;
 
-    boolean callConnected = false;
-
     private ServiceConnection mCallServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -285,6 +272,7 @@ public class CallSingleActivity extends AppCompatActivity {
         // 7,在 videoRoomCallback.onSubscribeAttached 中订阅发布者,如果有 publisher 的话
     }
 
+    @Override
     public void callOutSuccess(TcpModel tcpModel) {
         if (currentFragment != null) {
             currentFragment.callOutSuccess(tcpModel);
@@ -348,67 +336,44 @@ public class CallSingleActivity extends AppCompatActivity {
                 interactionVO = new Gson().fromJson(recTcpModel.getData().toString(), InteractionVO.class);
             }
 
-            if (isOutgoing && (recTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS || recTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT)) {
-                int toId;
-                if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
-                    toId = interactionVO.getToDeviceId();
-                } else {
-                    toId = interactionVO.getFromDeviceId();
-                }
-
-                TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
-                TcpCallback transaction = new TcpCallback(voiceUtilTcpModel.getTid()) {
-                    @Override
-                    public void onSuccess(JSONObject jsonObject) {
-                        //
-                    }
-
-                    @Override
-                    public void onFailed(JSONObject jsonObject) {
-                        //
+            if (isOutgoing) {
+                //去电
+                if (interactionVO != null) {
+                    int toId;
+                    if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                        toId = interactionVO.getToDeviceId();
+                    } else {
+                        toId = interactionVO.getFromDeviceId();
                     }
-                };
-                TcpClient.getInstance().sendTcp(voiceUtilTcpModel, false, transaction);
 
-                /*NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it-> {
-                    if (it) {
-                        Log.d(TAG, "TCP.发送消息完成");
-                    } else {
-                        Log.e(TAG, "TCP.发送消息失败");
-                        HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                    if (recTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
+                        //呼叫成功后取消呼叫
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceCancel(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
+                    } else if (recTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                        //对方接听了挂断
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
                     }
-                });*/
+                }
             } else if (recTcpModel.getAction() == TcpAction.VoiceAction.CALL) {
-                int toId;
-                if (interactionVO==null){
-                    toId = recTcpModel.getToId();
-                } else {
+                //来电
+                if (interactionVO != null) {
+                    int toId;
                     if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
                         toId = interactionVO.getToDeviceId();
                     } else {
                         toId = interactionVO.getFromDeviceId();
                     }
-                }
-                TcpModel voiceUtilTcpModel;
-                if (interactionVO == null){
-                    voiceUtilTcpModel = VoiceUtil.voiceCancel(recTcpModel.getTid(), Constants.Companion.getDeviceId(),recTcpModel.getToId(), null);
-                } else {
+
                     if (callConnected) {
-                        voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
                     } else {
-                        voiceUtilTcpModel = VoiceUtil.voiceReject(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceReject(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
                     }
                 }
-                TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
-
-                /*NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it-> {
-                    if (it) {
-                        Log.d(TAG, "TCP.发送消息完成");
-                    } else {
-                        Log.e(TAG, "TCP.发送消息失败");
-                        HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
-                    }
-                });*/
             }
         }
     }

+ 0 - 21
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java

@@ -284,16 +284,6 @@ public class FragmentAudio extends SingleCallFragment implements View.OnClickLis
         TcpClient.getInstance().sendTcp(voiceTransferTcpModel, false, null);
 
         handler.sendEmptyMessageDelayed(FINISH_CURRENT, 500);
-
-        /*NettyClient.Companion.getInstance().sendMsg(voiceTransferTcpModel.toJson()).subscribe(it-> {
-            if (it) {
-                Log.d(TAG, "TCP.发送消息完成");
-                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 500);
-            } else {
-                Log.e(TAG, "TCP.发送消息失败");
-                HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
-            }
-        });*/
     }
 
     public void acceptCall(){
@@ -338,17 +328,6 @@ public class FragmentAudio extends SingleCallFragment implements View.OnClickLis
             };
             TcpClient.getInstance().sendTcp(voiceUtilTcpModel, false, transaction);
 
-            /*TcpModel voiceUtilTcpModel = VoiceUtil.voiceAccept(Constants.Companion.getDeviceId(), toId, interactionVO.getId());
-            NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it->{
-                if (it) {
-                    Log.d(TAG, "TCP.发送消息完成");
-                } else {
-                    Log.e(TAG, "TCP.发送消息失败");
-                    callSingleActivity.loadingDialog.dismiss();
-                    HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
-                }
-            });*/
-
             transHandler.sendEmptyMessageDelayed(CHECK, 6000);
         }
     }

+ 414 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/FragmentSipAudio.java

@@ -0,0 +1,414 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.alibaba.fastjson.JSONObject;
+import com.blankj.utilcode.util.BarUtils;
+import com.enation.javashop.utils.base.tool.CommonTool;
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.component.home.util.Util;
+import com.wdkl.ncs.android.component.home.util.VoiceManagerUtil;
+import com.wdkl.ncs.android.lib.utils.ExtendMethodsKt;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpCallback;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum;
+import com.wdkl.ncs.android.middleware.utils.StringUtil;
+import com.wdkl.ncs.host.util.AudioRouteUtils;
+
+import org.linphone.core.Call;
+
+import java.util.Locale;
+
+/**
+ * 语音通话控制界面
+ */
+public class FragmentSipAudio extends BaseSipCallFragment implements View.OnClickListener {
+    private static final String TAG = "FragmentSipAudio";
+    private ImageView muteImageView;
+    private ImageView speakerImageView;
+    private boolean micEnabled = false; // 静音
+    private boolean isSpeakerOn = false; // 扬声器
+    private boolean accepted = false;
+
+    TransHandler transHandler;
+
+    @Override
+    int getLayout() {
+        return R.layout.fragment_audio;
+    }
+
+    @Override
+    public void initView(View view) {
+        super.initView(view);
+        muteImageView = view.findViewById(R.id.muteImageView);
+        speakerImageView = view.findViewById(R.id.speakerImageView);
+        minimizeImageView.setVisibility(View.GONE);
+        outgoingHangupImageView.setOnClickListener(this);
+        incomingHangupImageView.setOnClickListener(this);
+        incomingTransImageView.setOnClickListener(this);
+        minimizeImageView.setOnClickListener(this);
+        muteImageView.setOnClickListener(this);
+        acceptImageView.setOnClickListener(this);
+        speakerImageView.setOnClickListener(this);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            lytParent.post(() -> {
+                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) minimizeImageView.getLayoutParams();
+                params.topMargin = BarUtils.getStatusBarHeight();
+                minimizeImageView.setLayoutParams(params);
+            });
+        }
+    }
+
+    @Override
+    public void init() {
+        super.init();
+
+        if (callActivity.loadingDialog==null){
+            callActivity.loadingDialog = CommonTool.createLoadingDialog(this.getContext(), R.layout.custom_loading,R.id.loadding_image);
+        }
+        InteractionVO interactionVO = null;
+        if (callActivity.recTcpModel.getData()!=null) {
+            interactionVO = new Gson().fromJson(callActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        }
+        transHandler = new TransHandler();
+        //是来电,且是客户来电进入延时转接
+        if (!isOutgoing && interactionVO.getFromClerkId()==null) {
+            int transSeconds = SettingConfig.getCountdownTime(this.getContext());
+            transHandler.sendEmptyMessageDelayed(TRANS, transSeconds * 1000);
+        }
+
+        // 如果已经接通
+        if (core != null && core.getCurrentCall() != null && core.getCurrentCall().getState() == Call.State.Connected) {
+            descTextView.setVisibility(View.GONE); // 提示语
+            outgoingActionContainer.setVisibility(View.VISIBLE);
+            durationTextView.setVisibility(View.VISIBLE);
+            startRefreshTime();
+        } else {
+            // 如果未接通
+            if (isOutgoing) {
+                descTextView.setText(getShowName() + "\n" + StringUtil.getResString(R.string.call_in_calling));
+                outgoingActionContainer.setVisibility(View.VISIBLE);
+                incomingActionContainer.setVisibility(View.GONE);
+                //MediaPlayHelper.getInstance().playResMusic(R.raw.outgoing_call, 0.8f, true);
+            } else {
+                descTextView.setText(getShowName() + "\n" + StringUtil.getResString(R.string.call_incoming));
+                outgoingActionContainer.setVisibility(View.GONE);
+                incomingActionContainer.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    private String getShowName(){
+        InteractionVO interactionVO = null;
+        if (callActivity.recTcpModel.getData()!=null) {
+            interactionVO = new Gson().fromJson(callActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        }
+
+        String showName = "";
+        if (interactionVO==null){
+            showName = callActivity.showName;
+        } else {
+            //是自己拨出
+            if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                if (interactionVO.getToClerkId() == null) {
+                    showName = interactionVO.getToFrameFullName() + " " + interactionVO.getToMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_customer);
+                } else {
+                    showName = interactionVO.getToMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_clerk);
+                }
+            }
+            //是他人呼入
+            else {
+                //是客户
+                if (interactionVO.getFromClerkId() == null) {
+                    showName = interactionVO.getFromFrameFullName() /*+ " " + interactionVO.getFromMemberName()*/;
+                    portraitImageView.setImageResource(R.drawable.face_customer);
+
+                    if (!isOutgoing) {
+                        if (Constants.Companion.getRoleId() != null
+                            /*&& (Constants.Companion.getRoleId() == RoleTypeEnum.NURSE.value() || Constants.Companion.getRoleId() == RoleTypeEnum.NURSE_HEAD.value())*/) {
+                            if (interactionVO.getFromDeviceType() == DeviceTypeEnum.DIGIT_BED_DEVICE.value()
+                            || interactionVO.getFromDeviceType() == DeviceTypeEnum.SIMULATE_BED_DEVICE.value()) {
+                                //客户分机呼叫只能接听或转接,禁用拒接
+                                transLinearLayout.setVisibility(View.VISIBLE);
+                            } else {
+                                hangupLinearLayout.setVisibility(View.VISIBLE);
+                            }
+                        }
+                    }
+                }
+                //是同事
+                else {
+                    showName = interactionVO.getFromMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_clerk);
+
+                    if (!isOutgoing) {
+                        hangupLinearLayout.setVisibility(View.VISIBLE);
+                    }
+                }
+            }
+        }
+        return showName;
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onDestroy();
+    }
+
+    @Override
+    public void onDestroyView() {
+        transHandler.removeCallbacksAndMessages(null);
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (callActivity != null) {
+            if (core.getCurrentCall() != null && core.getCurrentCall().getState() == Call.State.Connected) {
+                //通话已连接上
+                showCallConnected();
+            }
+        }
+    }
+
+    @Override
+    void showCallConnected() {
+        Log.e(TAG, "show call connected");
+        if (callActivity.callConnected) {
+            return;
+        }
+        callActivity.callConnected = true;
+
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+
+        runOnUiThread(() -> {
+            callActivity.loadingDialog.dismiss();
+
+            incomingActionContainer.setVisibility(View.GONE);
+            outgoingActionContainer.setVisibility(View.VISIBLE);
+            //minimizeImageView.setVisibility(View.VISIBLE);
+            descTextView.setVisibility(View.GONE);
+
+            muteLinearLayout.setVisibility(View.VISIBLE);
+            speakerLinearLayout.setVisibility(View.VISIBLE);
+
+            isSpeakerOn = !isSpeakerOn;
+            if (VoiceManagerUtil.isHeadphonesPlugged(callActivity)) {
+                Log.d(TAG, "didChangeState 耳机" + isSpeakerOn);
+                AudioRouteUtils.Companion.routeAudioToHeadset(core, null, false);
+            } else {
+                Log.d(TAG, "didChangeState 外放切换" + isSpeakerOn);
+                if (isSpeakerOn) {
+                    AudioRouteUtils.Companion.routeAudioToSpeaker(core, null, false);
+                } else {
+                    AudioRouteUtils.Companion.routeAudioToEarpiece(core, null, false);
+                }
+                speakerImageView.setSelected(isSpeakerOn);
+            }
+
+            nameTextView.setText(getShowName());
+
+            startRefreshTime();
+        });
+    }
+
+    public static final int TRANS = 0x03;
+    public static final int CHECK = 0x04;
+
+    class TransHandler extends Handler {
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            if (core.getCurrentCall() != null) {
+                Call.State state = core.getCurrentCall().getState();
+                if (msg.what == TRANS) {
+                    if (callActivity != null && state != Call.State.Connected && !isHandoff) {
+                        transCall();
+                    }
+                } else if (msg.what == CHECK) {
+                    if (callActivity != null && state != Call.State.Connected && !isHandoff) {
+                        Toast.makeText(callActivity, R.string.call_disconnect, Toast.LENGTH_LONG).show();
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    private void transCall(){
+        callActivity.loadingDialog.show();
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+
+        //给服务器发送转接 tcp
+        InteractionVO interactionVO = new Gson().fromJson(callActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        int toId;
+        if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+            toId = interactionVO.getToDeviceId();
+        } else {
+            toId = interactionVO.getFromDeviceId();
+        }
+
+        TcpModel voiceTransferTcpModel = VoiceUtil.voiceTransfer(callActivity.recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+        TcpClient.getInstance().sendTcp(voiceTransferTcpModel, false, null);
+        handler.sendEmptyMessageDelayed(FINISH_CURRENT, 500);
+    }
+
+    public void acceptCall(){
+        if (callActivity!=null){
+            //取消自动转接
+            transHandler.removeMessages(TRANS);
+            accepted = true;
+
+            callActivity.loadingDialog.show();
+            callActivity.mVibrator.cancel();
+            MediaPlayHelper.getInstance().stopMusic(true);
+            RingPlayHelper.stopRingTone();
+            SpeechUtil.getInstance().stopSpeak();
+            //发出accept
+            InteractionVO interactionVO = new Gson().fromJson(callActivity.recTcpModel.getData().toString(), InteractionVO.class);
+            int toId;
+            if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                toId = interactionVO.getToDeviceId();
+            } else {
+                toId = interactionVO.getFromDeviceId();
+            }
+
+            TcpModel voiceUtilTcpModel = VoiceUtil.voiceAccept(callActivity.recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+            TcpCallback transaction = new TcpCallback(voiceUtilTcpModel.getTid()) {
+                @Override
+                public void onSuccess(JSONObject jsonObject) {
+                    //
+                }
+
+                @Override
+                public void onFailed(JSONObject jsonObject) {
+                    if (callActivity != null) {
+                        callActivity.runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                ExtendMethodsKt.showMessage("Accept failed");
+                                //callSingleActivity.loadingDialog.dismiss();
+                            }
+                        });
+                    }
+                }
+            };
+            
+            TcpClient.getInstance().sendTcp(voiceUtilTcpModel, false, transaction);
+            //transHandler.sendEmptyMessageDelayed(CHECK, 6000);
+        }
+    }
+
+    @Override
+    protected void handleHeadsetHook() {
+        //只有来电的时候才接听
+        if (!isOutgoing) {
+            if (accepted) {
+                if (callActivity != null) {
+                    isHandoff = true;
+                    transHandler.removeMessages(TRANS);
+                    callActivity.sendHandOffTcp();
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                }
+            } else {
+                acceptCall();
+            }
+        } else {
+            //去电时挂断
+            if (callActivity != null) {
+                isHandoff = true;
+                transHandler.removeMessages(TRANS);
+                callActivity.sendHandOffTcp();
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        Log.i(TAG, "keyup keyCode " + keyCode);
+        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            handleHeadsetHook();
+        } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+            return true;
+        }
+        return true;
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        // 转接
+        if (id == R.id.incomingTransImageView){
+            if (callActivity!=null) {
+                transCall();
+            }
+        }
+        // 接听
+        if (id == R.id.acceptImageView) {
+            acceptCall();
+        }
+        // 挂断电话
+        if (id == R.id.incomingHangupImageView || id == R.id.outgoingHangupImageView) {
+            if (callActivity != null) {
+                isHandoff = true;
+                //取消自动转接
+                transHandler.removeMessages(TRANS);
+                callActivity.sendHandOffTcp();
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+            }
+        }
+        // 静音
+        if (id == R.id.muteImageView) {
+            if (core != null) {
+                micEnabled = core.isMicEnabled();
+                Log.d(TAG,"静音切换" + micEnabled);
+                if (micEnabled) {
+                    muteImageView.setSelected(true);
+                    core.setMicEnabled(false);
+                } else {
+                    muteImageView.setSelected(false);
+                    core.setMicEnabled(true);
+                }
+            }
+        }
+        // 扬声器
+        if (id == R.id.speakerImageView) {
+            isSpeakerOn=!isSpeakerOn;
+            Log.d(TAG,"外放切换" + isSpeakerOn);
+            if (isSpeakerOn) {
+                AudioRouteUtils.Companion.routeAudioToSpeaker(core, null, false);
+            } else {
+                AudioRouteUtils.Companion.routeAudioToEarpiece(core, null, false);
+            }
+            speakerImageView.setSelected(isSpeakerOn);
+        }
+    }
+}

+ 1 - 73
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java

@@ -131,7 +131,6 @@ public abstract class SingleCallFragment extends Fragment {
         }
         if (durationTextView != null)
             durationTextView.stop();
-        refreshMessage(true);
         super.onDestroyView();
     }
 
@@ -225,18 +224,6 @@ public abstract class SingleCallFragment extends Fragment {
             }
         };
         TcpClient.getInstance().sendTcp(callSingleActivity.recTcpModel, false, transaction);
-
-        /*NettyClient.Companion.getInstance().sendMsg(callSingleActivity.recTcpModel.toJson()).subscribe(it -> {
-            if (it) {
-                Log.d(TAG, "TCP.发送消息完成");
-            } else {
-                Log.e(TAG, "TCP.发送消息失败");
-                HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
-                if (callSingleActivity != null) {
-                    callSingleActivity.finish();
-                }
-            }
-        });*/
     }
 
     public void callOutSuccess(TcpModel tcpModel) {
@@ -254,58 +241,6 @@ public abstract class SingleCallFragment extends Fragment {
         });
     }
 
-    // ======================================界面回调================================
-    public void didCallEndWithReason(EnumType.CallEndReason callEndReason) {
-        switch (callEndReason) {
-            case Busy: {
-                tvStatus.setText("对方忙线中");
-                break;
-            }
-            case SignalError: {
-                tvStatus.setText("连接断开");
-                break;
-            }
-            case RemoteSignalError: {
-                tvStatus.setText("对方网络断开");
-                break;
-            }
-            case Hangup: {
-                tvStatus.setText("挂断");
-                break;
-            }
-            case MediaError: {
-                tvStatus.setText("媒体错误");
-                break;
-            }
-            case RemoteHangup: {
-                tvStatus.setText("对方挂断");
-                break;
-            }
-            case OpenCameraFailure: {
-                tvStatus.setText("打开摄像头错误");
-                break;
-            }
-            case Timeout: {
-                tvStatus.setText("对方未接听");
-                break;
-            }
-            case AcceptByOtherClient: {
-                tvStatus.setText("在其它设备接听");
-                break;
-            }
-        }
-        incomingActionContainer.setVisibility(View.GONE);
-        outgoingActionContainer.setVisibility(View.GONE);
-        if (connectedActionContainer != null)
-            connectedActionContainer.setVisibility(View.GONE);
-        refreshMessage(false);
-        new Handler(Looper.getMainLooper()).postDelayed(() -> {
-            if (callSingleActivity != null) {
-                callSingleActivity.finish();
-            }
-
-        }, 11);
-    }
 
     public void didChangeState(EnumType.CallState state) {
 
@@ -317,13 +252,6 @@ public abstract class SingleCallFragment extends Fragment {
     public void didReceiveRemoteVideoTrack(BigInteger userId) {
     }
 
-    private void refreshMessage(Boolean isForCallTime) {
-        if (callSingleActivity == null) {
-            return;
-        }
-        // 刷新消息; demo中没有消息,不用处理这儿快逻辑
-    }
-
     public void startRefreshTime() {
         if (durationTextView != null) {
             leftTime = 0;
@@ -582,7 +510,7 @@ public abstract class SingleCallFragment extends Fragment {
                         }
                     });
                 //}
-            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL || tcpModel.getAction() == TcpAction.VoiceAction.VOICE_OFF) {
                 if (curInteractionId == Constants.Companion.getInteractionId()) {
                     handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
                     runOnUiThread(new Runnable() {

+ 374 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/ui/SipCallActivity.java

@@ -0,0 +1,374 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import androidx.fragment.app.FragmentManager;
+
+import com.enation.javashop.utils.base.tool.CommonTool;
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.service.FloatingService;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.ActivityStackUtil;
+import com.wdkl.ncs.android.component.home.util.HomeWatcher;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RecordHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.TcpClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.DeviceTypeEnum;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
+import com.wdkl.ncs.host.service.WdklSipService;
+
+import org.linphone.core.Call;
+import org.linphone.core.Core;
+
+import java.math.BigInteger;
+
+import pub.devrel.easypermissions.EasyPermissions;
+
+/**
+ * SIP通话界面
+ */
+public class SipCallActivity extends BaseCallActivity {
+
+    private static final String TAG = "SipCallActivity";
+
+    private boolean isOutgoing;
+    private BigInteger roomId;
+
+    boolean isAudioOnly = true;
+    boolean isSimulateBed = false;
+    private BaseSipCallFragment currentFragment;
+
+    private boolean mServiceBound = false;
+    private HomeWatcher mHomeWatcher;
+    public Core sipCore;
+
+    private ServiceConnection mCallServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "floating service connected");
+            // 获取服务的操作对象
+            FloatingService.MyBinder binder = (FloatingService.MyBinder) service;
+            binder.getService();
+        }
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "floating service disconnected");
+        }
+    };
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        //切换语言
+        int languageId = SettingConfig.getLanguageId(this);
+        LocaleMangerUtils.setApplicationLanguageByIndex(this, languageId);
+
+        super.onCreate(savedInstanceState);
+        setStatusBarOrScreenStatus(this);
+        setContentView(R.layout.activity_single_call);
+
+        DeviceChannel.calling = true;
+        sipCore = WdklSipService.getCore();
+
+        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+        loadingDialog = CommonTool.createLoadingDialog(this, R.layout.custom_loading,R.id.loadding_image);
+
+        final Intent intent = getIntent();
+        roomId = new BigInteger(intent.getStringExtra(EXTRA_ROOM_ID));
+        isOutgoing = intent.getBooleanExtra(EXTRA_MO, false);
+        isAudioOnly = intent.getBooleanExtra(EXTRA_AUDIO_ONLY,true);
+        recTcpModel = (TcpModel) intent.getSerializableExtra(EXTRA_TCPMODEL);
+        showName = intent.getStringExtra(EXTRA_SHOWNAME);
+
+        if (recTcpModel !=null && recTcpModel.getData() != null) {
+            Log.d(TAG, recTcpModel.toJson());
+            InteractionVO interactionVO = new Gson().fromJson(recTcpModel.getData().toString(), InteractionVO.class);
+            isSimulateBed = (interactionVO.getFromDeviceType() == DeviceTypeEnum.SIMULATE_BED_DEVICE.value());
+        }
+
+        // 权限检测
+        String[] perms;
+        if (isAudioOnly) {
+            perms = new String[]{Manifest.permission.RECORD_AUDIO};
+        } else {
+            perms = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
+        }
+        if (!EasyPermissions.hasPermissions(SipCallActivity.this, perms)) {
+            EasyPermissions.requestPermissions(SipCallActivity.this, "need record, camera permissions",
+                    100, perms);
+        } else {
+            init(roomId, isAudioOnly);
+        }
+
+        RecordHelper.getInstance().stopCancelRecordByOther(true);
+
+        //home和recent按键事件监听
+        mHomeWatcher = new HomeWatcher(this);
+        mHomeWatcher.setOnHomePressedListener(new HomeWatcher.OnHomePressedListener() {
+            @Override
+            public void onHomePressed() {
+                //如果悬浮窗没有显示 就开启服务展示悬浮窗
+                if (!Constants.Companion.getShowFloatWindow()) {
+                    startFloatingService();
+                }
+            }
+
+            @Override
+            public void onRecentAppPressed() {
+                //如果悬浮窗没有显示 就开启服务展示悬浮窗
+                if (!Constants.Companion.getShowFloatWindow()) {
+                    startFloatingService();
+                }
+            }
+        });
+        mHomeWatcher.startWatch();
+
+        ActivityStackUtil.setCallSingleActivity(this);
+    }
+
+    private void startFloatingService() {
+        //先检查权限
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (!Settings.canDrawOverlays(this)) {
+                Toast.makeText(SipCallActivity.this, "need floating window permission", Toast.LENGTH_LONG).show();
+                return;
+            }
+        }
+
+        //最小化Activity
+        moveTaskToBack(true);
+
+        //开启服务显示悬浮框
+        Intent floatVideoIntent = new Intent(this, FloatingService.class);
+        mServiceBound = bindService(floatVideoIntent, mCallServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        //界面恢复,移除悬浮框
+        if (mServiceBound) {
+            unbindService(mCallServiceConnection);
+            mServiceBound = false;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        //
+    }
+
+    public BaseSipCallFragment getCurrentFragment() {
+        return currentFragment;
+    }
+
+    public Vibrator getmVibrator() {
+        return mVibrator;
+    }
+
+    private void init(BigInteger roomId, boolean audioOnly) {
+        BaseSipCallFragment fragment = new FragmentSipAudio();
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        currentFragment = fragment;
+        fragmentManager.beginTransaction()
+                    .replace(R.id.fragment_content, fragment)
+                    .commitAllowingStateLoss();
+
+        if (!isOutgoing) {
+            long[] pattern = new long[]{100L, 2000L, 1000L, 2000L, 1000L, 2000L};
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                mVibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
+            } else {
+                mVibrator.vibrate(pattern, -1);
+            }
+        }
+    }
+
+    @Override
+    public void callOutSuccess(TcpModel tcpModel) {
+        if (currentFragment != null) {
+            currentFragment.callOutSuccess(tcpModel);
+        }
+    }
+
+    @TargetApi(19)
+    private static int getSystemUiVisibility() {
+        int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN |
+                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+        }
+        return flags;
+    }
+
+    /**
+     * 设置状态栏透明
+     */
+    @TargetApi(19)
+    public void setStatusBarOrScreenStatus(Activity activity) {
+        Window window = activity.getWindow();
+        //全屏+锁屏+常亮显示
+        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
+                //WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
+                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
+                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+        window.getDecorView().setSystemUiVisibility(getSystemUiVisibility());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
+            layoutParams.layoutInDisplayCutoutMode =
+                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+            window.setAttributes(layoutParams);
+        }
+        // 5.0以上系统状态栏透明
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            //清除透明状态栏
+            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+            //设置状态栏颜色必须添加
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+            window.setStatusBarColor(Color.TRANSPARENT);//设置透明
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19
+            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    public void sendHandOffTcp(){
+        if (recTcpModel.getType() == TcpType.VOICE) {
+            InteractionVO interactionVO = null;
+            if (recTcpModel.getData()!=null){
+                interactionVO = new Gson().fromJson(recTcpModel.getData().toString(), InteractionVO.class);
+            }
+
+            if (isOutgoing) {
+                //去电
+                if (interactionVO != null) {
+                    int toId;
+                    if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                        toId = interactionVO.getToDeviceId();
+                    } else {
+                        toId = interactionVO.getFromDeviceId();
+                    }
+
+                    if (recTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
+                        //呼叫成功后取消呼叫
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceCancel(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
+                    } else if (recTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                        //对方接听了挂断
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
+                    }
+                }
+            } else if (recTcpModel.getAction() == TcpAction.VoiceAction.CALL) {
+                //来电
+                if (interactionVO != null) {
+                    int toId;
+                    if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                        toId = interactionVO.getToDeviceId();
+                    } else {
+                        toId = interactionVO.getFromDeviceId();
+                    }
+
+                    if (callConnected) {
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
+                    } else {
+                        TcpModel voiceUtilTcpModel = VoiceUtil.voiceReject(recTcpModel.getTid(), Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                        TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson());
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event){
+        return currentFragment.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public synchronized void finish(){
+        Log.e(TAG, "call activity end");
+
+        if (sipCore != null && sipCore.getCallsNb() > 0) {
+            Call call = sipCore.getCurrentCall();
+            if (call != null) {
+                call.terminate();
+            } else if (sipCore.getCalls().length > 0) {
+                call = sipCore.getCalls()[0];
+                call.terminate();
+            }
+        }
+
+        if (loadingDialog != null) {
+            loadingDialog.dismiss();
+        }
+
+        if (mVibrator != null) {
+            mVibrator.cancel();
+        }
+
+        if (!isFinishing()) {
+            super.finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.d(TAG, "call activity destroyed");
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+        if (mHomeWatcher != null) {
+            mHomeWatcher.stopWatch();
+        }
+
+        DeviceChannel.calling = false;
+        DeviceChannel.callId = 0;
+
+        //解绑,不显示悬浮框
+        if (mServiceBound) {
+            unbindService(mCallServiceConnection);
+            mServiceBound = false;
+        }
+
+        ActivityStackUtil.setCallSingleActivity(null);
+
+        super.onDestroy();
+    }
+
+    public boolean isOutgoing() {
+        return isOutgoing;
+    }
+}

+ 4 - 4
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/util/ActivityStackUtil.java

@@ -1,15 +1,15 @@
 package com.wdkl.ncs.android.component.home.util;
 
-import com.wdkl.ncs.android.component.home.ui.CallSingleActivity;
+import com.wdkl.ncs.android.component.home.ui.BaseCallActivity;
 
 public class ActivityStackUtil {
-    private static CallSingleActivity callSingleActivity = null;
+    private static BaseCallActivity callSingleActivity = null;
 
-    public static void setCallSingleActivity(CallSingleActivity activity) {
+    public static void setCallSingleActivity(BaseCallActivity activity) {
         callSingleActivity = activity;
     }
 
-    public static CallSingleActivity getCallSingleActivity() {
+    public static BaseCallActivity getCallSingleActivity() {
         return callSingleActivity;
     }
 }

+ 241 - 0
android_mobile/src/main/yd_watch_2/code/com/wdkl/ncs/android/component/home/util/VoiceManagerUtil.java

@@ -0,0 +1,241 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHeadset;
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.os.Build;
+
+/**
+ * 类名称:VoiceManagerUtil <br>
+ * 类描述:声音控制工具类 <br>
+ * 创建人:Waderson Shll (TEL:15675117662)<br>
+ * 创建时间:2018-03-15 <br>
+ * 特别提醒:如有需要该类可任意创建与调用;在未通知本人的情况下该类禁止任何修改!<br>
+ */
+public class VoiceManagerUtil {
+    /**
+     * 获取提示音音量最大值
+     *
+     * @param context
+     */
+    public static int getAlarmMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
+    }
+
+    /**
+     * 获取提示音音量当前值
+     *
+     * @param context
+     */
+    public static int getAlarmNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+    }
+
+    /**
+     * 获取多媒体音量最大值
+     *
+     * @param context
+     */
+    public static int getMusicMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+    }
+
+    /**
+     * 获取多媒体音量当前值
+     *
+     * @param context
+     */
+    public static int getMusicNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+    }
+
+    /**
+     * 获取铃声音量最大值
+     *
+     * @param context
+     */
+    public static int getRingMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
+    }
+
+    /**
+     * 获取铃声音量当前值
+     *
+     * @param context
+     */
+    public static int getRingNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
+    }
+
+    /**
+     * 获取系统音量最大值
+     *
+     * @param context
+     */
+    public static int getSystemMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
+    }
+
+    /**
+     * 获取系统音量当前值
+     *
+     * @param context
+     */
+    public static int getSystemNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
+    }
+
+    /**
+     * 获取通话音量最大值
+     *
+     * @param context
+     */
+    public static int getCallMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
+    }
+
+    /**
+     * 获取通话音量当前值
+     *
+     * @param context
+     */
+    public static int getCallNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+    }
+
+    /**
+     * 设置提示音音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setAlarmVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_ALARM, (int) (getAlarmMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置多媒体音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setMusicVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) (getMusicMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置铃声音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setRingVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_RING, (int) (getRingMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置系统音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setSystemVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, (int) (getSystemMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置通话音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setCallVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, (int) (getCallMax(context) * vPercent), 0);
+    }
+
+    public static void setSpeakerOn(Context context, boolean speakerOn) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setSpeakerphoneOn(speakerOn);
+    }
+
+    public static void toggleHeadset(Context context, boolean isHeadset) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        if (audioManager != null) {
+            if (isHeadset) {
+                audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+                audioManager.setSpeakerphoneOn(false);
+            } else {
+                audioManager.setSpeakerphoneOn(true);
+            }
+        }
+    }
+
+    public static void setAudioMode(Context context, int mode) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setMode(mode);
+    }
+
+    public static void setAudioMute(Context context, boolean mute) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setMicrophoneMute(mute);
+    }
+
+    /**
+     * 蓝牙是否连接
+     * @return
+     */
+    public static boolean isBluetoothHeadsetConnected(){
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        return (bluetoothAdapter != null && bluetoothAdapter.isEnabled()
+                && bluetoothAdapter.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED);
+    }
+
+    public static boolean isHeadphonesPlugged(Context context) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            @SuppressLint("WrongConstant") AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+            for (AudioDeviceInfo deviceInfo : audioDevices) {
+                if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
+                        || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return audioManager.isWiredHeadsetOn();
+        }
+    }
+}

+ 22 - 0
android_mobile/src/main/yd_watch_2/res/layout/user_setting_layout.xml

@@ -189,6 +189,28 @@
                         android:textSize="14dp" />
                 </LinearLayout>
 
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/voice_type"
+                        android:textColor="@color/javashop_color_searcher_tv_gray"
+                        android:textSize="14dp" />
+
+                    <TextView
+                        android:id="@+id/tv_voice_type"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:gravity="right"
+                        android:textColor="@color/javashop_color_searcher_tv_gray"
+                        android:textSize="14dp" />
+                </LinearLayout>
+
             </LinearLayout>
 
             <RelativeLayout

+ 3 - 0
app/src/main/code/com/wdkl/app/ncs/application/Application.kt

@@ -7,6 +7,7 @@ import com.enation.javashop.android.jrouter.JRouter
 import com.enation.javashop.net.engine.config.NetEngineConfig
 import com.enation.javashop.net.engine.plugin.exception.RestfulExceptionInterceptor
 import com.enation.javashop.utils.base.config.BaseConfig
+import com.hjq.toast.Toaster
 import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
 import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils
 import com.wdkl.ncs.android.lib.base.BaseApplication
@@ -73,6 +74,8 @@ class Application : BaseApplication() {
         NetEngineConfig.init(baseContext)
                 .openLogger()
                 .addNetInterceptor(RestfulExceptionInterceptor())
+
+        Toaster.init(this)
     }
 
     override fun attachBaseContext(base: Context?) {

+ 3 - 0
common/build.gradle

@@ -149,6 +149,9 @@ dependencies {
     //linphone sip sdk
     api(name: 'linphone-sdk-android-5.2.10', ext: 'aar')
 
+    //toast 框架
+    compile files('libs/Toaster-12.6.aar')
+
     /**
      *  constraint-layout布局依赖
      */

BIN
common/libs/Toaster-12.6.aar


+ 10 - 2
common/src/main/code/com/wdkl/ncs/android/lib/utils/ExtendMethods.kt

@@ -31,6 +31,7 @@ import com.enation.javashop.net.engine.plugin.rxbus.RxBus
 import com.enation.javashop.utils.base.tool.CommonTool
 import com.enation.javashop.utils.base.tool.ScreenTool
 import com.enation.javashop.utils.logger.LoggerFactory
+import com.hjq.toast.Toaster
 import com.wdkl.ncs.android.lib.R
 import io.reactivex.disposables.Disposable
 import okhttp3.ResponseBody
@@ -85,7 +86,14 @@ fun showMessage(message: String) {
     if (message == ""){
         return
     }
-    val messageCallback = {
+
+    try {
+        Toaster.showShort(message)
+    } catch (ex: Exception) {
+        //
+    }
+
+    /*val messageCallback = {
         Toast.makeText(BaseApplication.appContext, message, Toast.LENGTH_SHORT).show()
     }
     try {
@@ -93,7 +101,7 @@ fun showMessage(message: String) {
     } catch (runtime: RuntimeException) {
         Looper.prepare()
         messageCallback.invoke()
-    }
+    }*/
 }
 
 fun showMessage(resId: Int) {

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

@@ -42,7 +42,7 @@ class Constants {
         var allowVoiceMsg = true
 
         //是否支持语音通话
-        val supportCall = false
+        val supportCall = true
 
         //电话状态
         const val PHONE_IDLE = 0