package com.ydl.audioim import android.annotation.SuppressLint import android.content.Context import android.text.TextUtils import com.alibaba.android.arouter.launcher.ARouter import com.google.gson.Gson import com.ydl.audioim.http.AudioApiRequestUtil import com.ydl.audioim.http.command.ConnectExceptionCommand import com.ydl.consultantim.ConsultantAudioHomeActivity import com.ydl.ydl_av.chat.bean.AudioMessageBean import com.ydl.ydl_av.messge_service.YDLRTMClient import com.ydl.ydl_av.messge_service.bean.RTMMesssage import com.ydl.ydl_av.messge_service.callback.CallListener import com.ydl.ydl_av.messge_service.callback.InitListener import com.ydl.ydl_av.messge_service.callback.LoginCallback import com.ydl.ydl_av.messge_service.request.LoginParam import com.ydl.ydl_av.messge_service.response.CallLocalResponse import com.ydl.ydl_av.messge_service.response.CallRemoteResponse import com.ydl.ydlcommon.modular.ModularServiceManager import com.ydl.ydlcommon.utils.ActivityManager import com.ydl.ydlcommon.utils.LogUtil import com.ydl.ydlcommon.utils.log.LogHelper import com.yidianling.common.tools.ToastUtil import com.yidianling.user.api.service.IUserService import io.agora.rtm.RtmStatusCode import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import java.util.concurrent.TimeUnit /** * @author harvie * @date 2019/9/27 * 语音通话入口类 */ class YDLavManager { companion object { val instances: YDLavManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { YDLavManager() } } private constructor() fun init(context: Context, appId: String) { YDLRTMClient.instances.init(context, appId, listener) //设置回调 setCallback() } private fun setCallback() { YDLRTMClient.instances.setCallListener(object : CallListener { override fun onCallRecivedByPeer(response: CallLocalResponse?) { //返回给主叫:被叫已收到呼叫邀请 LogUtil.e("[agora]${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}") val act = ActivityManager.getInstance().getTopTaskActivity() if (act is AudioHomeActivity) { act.runOnUiThread { act.playWaitingMusic() } } } override fun onCallAccepted(response: CallLocalResponse?, msg: String?) { //返回给主叫 LogUtil.e("[agora]${response?.calleeId}已接收呼叫邀请") //加入声网频道时机修改:用户收到专家接受邀请的回调后再加入声网频道 val act = ActivityManager.getInstance().getTopTaskActivity() if (act is AudioHomeActivity) { act.runOnUiThread { act.joinChannel() } } } override fun onCallRefused(response: CallLocalResponse?, msg: String?) { //返回给主叫 LogUtil.e("[agora]${response?.calleeId}已拒绝呼叫邀请") val act = ActivityManager.getInstance().getTopTaskActivity() if (act is AudioHomeActivity) { act.runOnUiThread { ToastUtil.toastShort("对方已挂断") //通话结束或挂断时,上传日志文件 act.uploadLog() act.leaveChannel() } } } override fun onCallCanceled(response: CallLocalResponse?) { //返回给主叫 LogUtil.e("[agora]主叫已取消呼叫邀请") } override fun onCallFailure(response: CallLocalResponse?, errorCode: Int) { //返回给主叫 LogUtil.e("[agora]呼叫${response?.calleeId}用户失败:${response?.response}") //专家离线或者30 秒后仍未收到专家响应,重新再邀请一次 when (errorCode) { //被叫不在线 呼叫邀请发出 30 秒后被叫仍未 ACK 响应呼叫邀请 RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_PEER_OFFLINE, RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_PEER_NO_RESPONSE -> { val act = ActivityManager.getInstance().getTopTaskActivity() if (act is AudioHomeActivity) { act.runOnUiThread { act.rtcCall() } } } RtmStatusCode.LocalInvitationError.LOCAL_INVITATION_ERR_INVITATION_EXPIRE -> {//呼叫邀请过期。被叫 ACK 响应呼叫邀请后 60 秒呼叫邀请未被取消、接受、拒绝,则呼叫邀请过期。 } } } override fun onRemoteInvitationReceived(response: CallRemoteResponse?) { //返回给被叫 LogUtil.e("[agora]收到来自${response?.callerId}的呼叫邀请") receivedCall(response?.content) } override fun onRemoteInvitationAccepted(response: CallRemoteResponse?) { //返回给被叫 LogUtil.e("[agora]接受来自${response?.callerId}的呼叫成功") } override fun onRemoteInvitationRefused(response: CallRemoteResponse?) { //返回给被叫 LogUtil.e("[agora]已拒绝来自${response?.callerId}的呼叫") } override fun onRemoteInvitationCanceled(response: CallRemoteResponse?) { //返回给被叫 LogUtil.e("[agora]主叫${response?.callerId}已取消呼叫邀请") closePage() } override fun onRemoteInvitationFailure(response: CallRemoteResponse?, errorCode: Int) { //返回给被叫 LogUtil.e("[agora]来自主叫${response?.callerId}的呼叫邀请进程失败:${response?.response}") //关闭页面 closePage() } override fun onOtherMsg(error: String?) { LogUtil.e("[agora]其它消息:${error}") } }) } @SuppressLint("CheckResult") fun login(userId: String?) { if (TextUtils.isEmpty(userId) || userId ?: "0" <= "0") { //如果uid为空或小于等于0 ,则不进行登录,因为uid为0也会登录成功,会导致后面uid正确时无法登录 LogUtil.e("[agora]login-uid:$userId") return } //登录实时消息 //获取token AudioApiRequestUtil.getAgoraToken() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ if ("200".equals(it.code)) { YDLRTMClient.instances.login(LoginParam(userId, it.data.token), object : LoginCallback { override fun onSuccess() { //登陆成功,发起呼叫 LogUtil.e("[agora]实时消息登录成功") } override fun onFailure(msg: String?) { LogUtil.e("[agora]实时消息登录失败:$msg") } }) } else { LogUtil.e("声网token获取失败uid:" + userId + " error:" + it.msg) LogHelper.getInstance() .writeLogSync("声网token获取失败uid:" + userId + " error:" + it.msg) } }, { LogUtil.e("声网token获取异常uid:" + userId + " error:" + it.message) }) } /** * 收到邀请 */ @SuppressLint("CheckResult") fun receivedCall(content: String?) { if (!TextUtils.isEmpty(content)) { //如果已经接听了用户电话 再有电话进来 是不能接听的 if (!activityIsExists(ConsultantAudioHomeActivity::class.java) && !activityIsExists( AudioHomeActivity::class.java ) ) { //延时启动通话界面,防止刚打开就被main遮挡 Observable.timer(1000, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { LogUtil.e("[agora]启动通话界面") //邀请加入频道消息,跳转通话界面 ARouter.getInstance().build("/av/ConsultantAudioHomeActivity") .withString("param", content) .navigation() } } else { try { val mAudioMessageBean = Gson().fromJson(content, AudioMessageBean::class.java) YDLRTMClient.instances.refuseCall(mAudioMessageBean.channelId) } catch (e: Exception) { } LogUtil.d("[agora]收到声网邀请,但界面实例已存在") } } else { LogUtil.d("[agora]收到声网邀请,但response==null") } } private fun activityIsExists(cls: Class<*>): Boolean { for (activity in ActivityManager.getInstance().getActivitys()) { if (activity.javaClass == cls) { return true } } return false } /** * 关闭通话界面 */ fun closePage() { var act = ActivityManager.getInstance().getTopTaskActivity() if (act is ConsultantAudioHomeActivity) { //未接通时,收到呼叫进程失败关闭页面,已接通无需关闭 if (act.status == ConsultantAudioHomeActivity.STATUS_NOT_ANSWERED) { act.close(ConsultantAudioHomeActivity.RESULT_USER_CANCEL, "") } } } /** * 退出登录 */ fun logout() { YDLRTMClient.instances.logout(object : LoginCallback { override fun onSuccess() { //退出登陆成功 LogUtil.d("[agora]实时消息退出成功") } override fun onFailure(msg: String?) { LogUtil.d("[agora]实时消息退出失败:$msg") } }) } /** * RTM登录异常,上传错误日志 msg * 声网出现异常,上传错误日志 connectException */ @SuppressLint("CheckResult") fun uploadException(connectException: ConnectExceptionCommand) { AudioApiRequestUtil.connectException(connectException) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ }, { LogUtil.e("agora", "声网上传异常与错误日志接口调用失败:" + it.message) }) } /** * 实时消息全局监听 */ private val listener = object : InitListener { override fun onTokenExpired() { LogUtil.e("[agora]onTokenExpired") instances.login(ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid) } override fun onMessageReceived(message: RTMMesssage, userId: Int) { LogUtil.i("[agora]onMessageReceived:${message.text} -->uid:$userId") } override fun onConnectionStateChanged(state: Int, reason: Int) { LogUtil.i("[agora]onConnectionStateChanged:state:${state} -->reason:$reason") } } }