Commit 250613cb by 刘鹏

Merge branch 'release' into feat/lp/lp_expert_status

parents 325d7be9 6b85513f
...@@ -22,21 +22,36 @@ interface IAudioImService : IProvider{ ...@@ -22,21 +22,36 @@ interface IAudioImService : IProvider{
/** /**
* rtm登录返回并登录状态 * rtm登录返回并登录状态
*/ */
fun loginRtm(userId:String?,event: (isSuccess: Boolean, msg: String?) -> Unit) fun loginRtm(userId: String?, event: (isSuccess: Boolean, msg: String?) -> Unit)
/** /**
* rtm登录 * rtm登录
*/ */
fun loginRtm(userId:String?) fun loginRtm(userId: String?)
/**
* rtm退出登录
*/
fun logoutRtm()
/** /**
* 拨打语音电话转axb弹窗 * 拨打语音电话转axb弹窗
*/ */
fun openAxbDialog(activity: Activity?, type:Int, phoneNumber: String?) fun openAxbDialog(activity: Activity?, type: Int, phoneNumber: String?)
fun openPermissionRejectDialog(activity: Activity?, callback: (() -> Unit))
/** /**
* 事件上报 * 事件上报
*/ */
fun callEventSave(status: String, res: String, session: String?, line: String) fun callEventSave(status: String, res: String, session: String?, line: String)
fun initRtcNetQuality()
fun initAgoraRtc(context: Context)
fun reportCallEvent(type: String, name: String, desc: String, retCode: Int?)
fun callCheck(context: Context, calleeUid: String? = null, delay: Long = 0)
} }
\ No newline at end of file
...@@ -190,4 +190,6 @@ interface IImService : IProvider { ...@@ -190,4 +190,6 @@ interface IImService : IProvider {
fun dismissConsultServiceDialog(); fun dismissConsultServiceDialog();
/**群聊*/ /**群聊*/
fun startTeamSession(activity: Activity, tid: String, doctorId: String) fun startTeamSession(activity: Activity, tid: String, doctorId: String)
fun getSystemConfig(key:String)
} }
\ No newline at end of file
...@@ -37,7 +37,7 @@ android { ...@@ -37,7 +37,7 @@ android {
multiDexEnabled true multiDexEnabled true
ndk { ndk {
abiFilters "arm64-v8a","armeabi-v7a" // 指定要ndk需要兼容的架构(这样其他依赖包里mips,x86,armeabi,arm-v8之类的so会被过滤掉) abiFilters "arm64-v8a"/*,"armeabi-v7a"*/ // 指定要ndk需要兼容的架构(这样其他依赖包里mips,x86,armeabi,arm-v8之类的so会被过滤掉)
} }
} }
...@@ -168,6 +168,8 @@ android { ...@@ -168,6 +168,8 @@ android {
packagingOptions { packagingOptions {
exclude 'META-INF/proguard/coroutines.pro' exclude 'META-INF/proguard/coroutines.pro'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
} }
dataBinding { dataBinding {
enabled true enabled true
...@@ -177,6 +179,9 @@ android { ...@@ -177,6 +179,9 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
debugImplementation('com.ydl.debugkit:debugkit:1.0.0'){
exclude group: "androidx.sqlite"
}
implementation 'com.github.feeeei:CircleSeekbar:v1.1.2' implementation 'com.github.feeeei:CircleSeekbar:v1.1.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation(rootProject.ext.dependencies["appcompat-v7"]) implementation(rootProject.ext.dependencies["appcompat-v7"])
...@@ -213,6 +218,7 @@ dependencies { ...@@ -213,6 +218,7 @@ dependencies {
implementation project(':m-muse') implementation project(':m-muse')
implementation project(':m-im') implementation project(':m-im')
implementation project(":api:im") implementation project(":api:im")
implementation project(":api:audioim")
implementation project(':m-dynamic') implementation project(':m-dynamic')
implementation project(':m-course') implementation project(':m-course')
implementation project(":api:course") implementation project(":api:course")
......
...@@ -27,7 +27,7 @@ import com.ydl.component.mvp.DemoContract ...@@ -27,7 +27,7 @@ import com.ydl.component.mvp.DemoContract
import com.ydl.component.mvp.DemoPresenter import com.ydl.component.mvp.DemoPresenter
import com.ydl.component.route.PlatformTempCommonRouteImpl import com.ydl.component.route.PlatformTempCommonRouteImpl
import com.ydl.component.rtc.MDTLoginActivity import com.ydl.component.rtc.MDTLoginActivity
import com.ydl.confide.home.ConfideHomeActivity import com.ydl.confide.api.ConfideRoute
import com.ydl.ydlcommon.data.http.ThrowableConsumer import com.ydl.ydlcommon.data.http.ThrowableConsumer
import com.ydl.ydlcommon.modular.ModularServiceManager import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
...@@ -179,7 +179,8 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -179,7 +179,8 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
YDLavManager.instances.login( YDLavManager.instances.login(
ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid
) )
startActivity(Intent(this, ConfideHomeActivity::class.java)) ARouter.getInstance().build(Uri.parse(ConfideRoute.R_CONFIDE_HOME+"?confidedId=299")).navigation()
// startActivity(Intent(this, ConfideHomeActivity::class.java))
} }
ModularServiceManager.provide(IConsultantService::class.java).requestGuideData() ModularServiceManager.provide(IConsultantService::class.java).requestGuideData()
......
...@@ -4,9 +4,10 @@ import android.app.ActivityManager; ...@@ -4,9 +4,10 @@ import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Process; import android.os.Process;
import androidx.multidex.MultiDex;
import android.webkit.WebView; import android.webkit.WebView;
import androidx.multidex.MultiDex;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
import com.meituan.android.walle.WalleChannelReader; import com.meituan.android.walle.WalleChannelReader;
import com.tencent.qcloud.tuicore.calling.trtccalling.model.TRTCCalling; import com.tencent.qcloud.tuicore.calling.trtccalling.model.TRTCCalling;
...@@ -14,6 +15,7 @@ import com.umeng.analytics.MobclickAgent; ...@@ -14,6 +15,7 @@ import com.umeng.analytics.MobclickAgent;
import com.umeng.commonsdk.UMConfigure; import com.umeng.commonsdk.UMConfigure;
import com.umeng.socialize.PlatformConfig; import com.umeng.socialize.PlatformConfig;
import com.umeng.socialize.UMShareAPI; import com.umeng.socialize.UMShareAPI;
import com.ydl.audioim.api.IAudioImService;
import com.ydl.component.route.PlatformTempCommonRouteImpl; import com.ydl.component.route.PlatformTempCommonRouteImpl;
import com.ydl.confide.BuildConfig; import com.ydl.confide.BuildConfig;
import com.ydl.media.audio.AudioPlayer; import com.ydl.media.audio.AudioPlayer;
...@@ -21,6 +23,7 @@ import com.ydl.media.audio.manager.MediaSessionManager; ...@@ -21,6 +23,7 @@ import com.ydl.media.audio.manager.MediaSessionManager;
import com.ydl.media.audio.manager.NotifyManager; import com.ydl.media.audio.manager.NotifyManager;
import com.ydl.ydl_image.manager.YDLImageCacheManager; import com.ydl.ydl_image.manager.YDLImageCacheManager;
import com.ydl.ydlcommon.base.BaseApp; import com.ydl.ydlcommon.base.BaseApp;
import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.ydl.ydlcommon.router.YdlCommonRouterManager; import com.ydl.ydlcommon.router.YdlCommonRouterManager;
import com.ydl.ydlcommon.utils.AppProgressUtils; import com.ydl.ydlcommon.utils.AppProgressUtils;
import com.ydl.ydlcommon.utils.Utils; import com.ydl.ydlcommon.utils.Utils;
...@@ -29,7 +32,6 @@ import com.yidianling.common.tools.ToastUtil; ...@@ -29,7 +32,6 @@ import com.yidianling.common.tools.ToastUtil;
import com.yidianling.consultant.preview.TestImageLoader; import com.yidianling.consultant.preview.TestImageLoader;
import com.yidianling.consultant.preview.ZoomMediaLoader; import com.yidianling.consultant.preview.ZoomMediaLoader;
import com.yidianling.course.lifeCallback.CoursePlayLifecycle; import com.yidianling.course.lifeCallback.CoursePlayLifecycle;
import com.yidianling.course.widget.AudioPlayView;
/** /**
...@@ -71,12 +73,14 @@ public class ComponentTestApp extends BaseApp { ...@@ -71,12 +73,14 @@ public class ComponentTestApp extends BaseApp {
registerActivityLifecycleCallbacks(new CoursePlayLifecycle()); registerActivityLifecycleCallbacks(new CoursePlayLifecycle());
ModularServiceManagerKt.findRouteService(IAudioImService.class).initAgoraRtc(this);
Fresco.initialize(this); Fresco.initialize(this);
TRTCCalling.sharedInstance(this).setMdtCallBack(new TRTCCalling.MdtCallBack() { TRTCCalling.sharedInstance(this).setMdtCallBack(new TRTCCalling.MdtCallBack() {
@Override @Override
public void onReceiveNewInvitation(String roomId) { public void onReceiveNewInvitation(String roomId) {
ToastUtil.toastShort("电话邀请"+roomId); ToastUtil.toastShort("电话邀请" + roomId);
} }
@Override @Override
......
...@@ -14,7 +14,7 @@ buildscript { ...@@ -14,7 +14,7 @@ buildscript {
ydlrouter_version = '1.2.3' ydlrouter_version = '1.2.3'
constrait_support_version = '1.0.2' constrait_support_version = '1.0.2'
componentVersion = "0.3.0.44" componentVersion = "0.3.0.48"
} }
repositories { repositories {
mavenCentral() mavenCentral()
......
...@@ -65,7 +65,7 @@ dependencies { ...@@ -65,7 +65,7 @@ dependencies {
kapt "com.alibaba:arouter-compiler:$arouter_compiler" kapt "com.alibaba:arouter-compiler:$arouter_compiler"
api "com.alibaba:arouter-api:$arouter_api" api "com.alibaba:arouter-api:$arouter_api"
api "com.ydl:ydl-av:1.4.6" api "com.ydl:ydl-av:1.4.8"
implementation 'com.volcengine:apm_insight:1.4.6.cn' implementation 'com.volcengine:apm_insight:1.4.6.cn'
api project(':ydl-platform') api project(':ydl-platform')
......
...@@ -10,6 +10,7 @@ import android.hardware.Sensor ...@@ -10,6 +10,7 @@ import android.hardware.Sensor
import android.hardware.SensorEvent import android.hardware.SensorEvent
import android.hardware.SensorEventListener import android.hardware.SensorEventListener
import android.hardware.SensorManager import android.hardware.SensorManager
import android.media.AudioManager
import android.media.MediaPlayer import android.media.MediaPlayer
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
...@@ -18,6 +19,7 @@ import android.text.TextUtils ...@@ -18,6 +19,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
...@@ -28,6 +30,10 @@ import com.hjq.permissions.XXPermissions ...@@ -28,6 +30,10 @@ import com.hjq.permissions.XXPermissions
import com.ydl.audioim.bean.AgoraInvitationBean import com.ydl.audioim.bean.AgoraInvitationBean
import com.ydl.audioim.bean.AgoraLogInfoBean import com.ydl.audioim.bean.AgoraLogInfoBean
import com.ydl.audioim.contract.IAudioHomeActivityContract import com.ydl.audioim.contract.IAudioHomeActivityContract
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.AudioNetAPi
import com.ydl.audioim.http.LeaveChannelReason
import com.ydl.audioim.http.RtcEvent
import com.ydl.audioim.http.command.ConnectCommand import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.ConnectExceptionCommand import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.http.command.NoticePushCommand import com.ydl.audioim.http.command.NoticePushCommand
...@@ -41,6 +47,7 @@ import com.ydl.audioim.utils.DateUtils ...@@ -41,6 +47,7 @@ import com.ydl.audioim.utils.DateUtils
import com.ydl.audioim.utils.onConfideEvent import com.ydl.audioim.utils.onConfideEvent
import com.ydl.audioim.widget.AxbConfirmDialog import com.ydl.audioim.widget.AxbConfirmDialog
import com.ydl.audioim.widget.ZDialog import com.ydl.audioim.widget.ZDialog
import com.ydl.devicesidlib.DeviceIDHelper
import com.ydl.webview.H5Params import com.ydl.webview.H5Params
import com.ydl.webview.NewH5Activity import com.ydl.webview.NewH5Activity
import com.ydl.webview.RefreshWebEvent import com.ydl.webview.RefreshWebEvent
...@@ -60,7 +67,9 @@ import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils ...@@ -60,7 +67,9 @@ import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.ydl.ydlcommon.utils.log.AliYunLogConfig import com.ydl.ydlcommon.utils.log.AliYunLogConfig
import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper
import com.ydl.ydlcommon.utils.log.LogHelper import com.ydl.ydlcommon.utils.log.LogHelper
import com.ydl.ydlcommon.utils.log.XLog
import com.ydl.ydlcommon.utils.remind.ToastHelper import com.ydl.ydlcommon.utils.remind.ToastHelper
import com.ydl.ydlnet.YDLHttpUtils
import com.yidianling.common.tools.ToastUtil import com.yidianling.common.tools.ToastUtil
import com.yidianling.im.api.bean.IMRegisterObserverCustomNotificationCallBack import com.yidianling.im.api.bean.IMRegisterObserverCustomNotificationCallBack
import com.yidianling.im.api.service.IImService import com.yidianling.im.api.service.IImService
...@@ -69,8 +78,10 @@ import de.greenrobot.event.EventBus ...@@ -69,8 +78,10 @@ import de.greenrobot.event.EventBus
import io.agora.rtc.Constants import io.agora.rtc.Constants
import io.agora.rtc.IRtcEngineEventHandler import io.agora.rtc.IRtcEngineEventHandler
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.audioim_activity_audio_home.* import kotlinx.android.synthetic.main.audioim_activity_audio_home.*
import java.util.* import java.util.*
...@@ -233,6 +244,16 @@ class AudioHomeActivity : ...@@ -233,6 +244,16 @@ class AudioHomeActivity :
private var canChangeRoute: Boolean = false private var canChangeRoute: Boolean = false
private var hasHandleRefused = false private var hasHandleRefused = false
private var hasJoinChannel = false
private val tvNetDelay: TextView by lazy { findViewById(R.id.tvDelay) }
private val tvNetDown: TextView by lazy { findViewById(R.id.tvDown) }
private val tvNetUp: TextView by lazy { findViewById(R.id.tvUp) }
private val ivNetQuality: ImageView by lazy { findViewById(R.id.ivQuality) }
private val tvNetQuality: TextView by lazy { findViewById(R.id.tvQuality) }
private val layoutQualityGroup: View by lazy { findViewById(R.id.layoutQualityGroup) }
private val notificationCallback = object : IMRegisterObserverCustomNotificationCallBack { private val notificationCallback = object : IMRegisterObserverCustomNotificationCallBack {
override fun onObserverCustomNotification(fromUid: String, toUid: String, content: String) { override fun onObserverCustomNotification(fromUid: String, toUid: String, content: String) {
if (fromUid == listenerUid) { if (fromUid == listenerUid) {
...@@ -259,12 +280,12 @@ class AudioHomeActivity : ...@@ -259,12 +280,12 @@ class AudioHomeActivity :
runOnUiThread { runOnUiThread {
if (hasHandleRefused) return@runOnUiThread if (hasHandleRefused) return@runOnUiThread
hasHandleRefused = true hasHandleRefused = true
YDLavManager.instances.callEndStatusUpdate(channelId ?: "", 2, "被叫拒绝") YDLavManager.instances.callEndStatusUpdate(channelId, 2, "被叫拒绝")
ToastUtil.toastShort("对方已挂断") ToastUtil.toastShort("对方已挂断")
writeAgoraLog("被叫(专家)拒绝了通话邀请") writeAgoraLog("被叫(专家)拒绝了通话邀请")
//通话结束或挂断时,上传日志文件 //通话结束或挂断时,上传日志文件
uploadLog() uploadLog()
leaveChannel() leaveChannel(1)
uploadExceptionStatus("对方已拒绝", 2) uploadExceptionStatus("对方已拒绝", 2)
} }
} }
...@@ -319,7 +340,8 @@ class AudioHomeActivity : ...@@ -319,7 +340,8 @@ class AudioHomeActivity :
return return
} }
Apm.reportEvent("agora_android", "occur_error", "$err") Apm.reportEvent("agora_android", "occur_error", "$err")
channelId?.let { YDLavManager.instances.callEndStatusUpdate(it, 4, "频道的错误回调信息$err") } AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.errorOccurred), errorCode = err)
channelId?.let { YDLavManager.instances.callEndStatusUpdate(it, 60, "频道错误回调$err") }
} }
override fun onApiCallExecuted(error: Int, api: String?, result: String?) { override fun onApiCallExecuted(error: Int, api: String?, result: String?) {
...@@ -334,6 +356,7 @@ class AudioHomeActivity : ...@@ -334,6 +356,7 @@ class AudioHomeActivity :
super.onJoinChannelSuccess(channel, uid, elapsed) super.onJoinChannelSuccess(channel, uid, elapsed)
onMeJoined() onMeJoined()
callEventSave("20", "$uid 用户声网加入频道成功:channel=$channel") callEventSave("20", "$uid 用户声网加入频道成功:channel=$channel")
AudioApiRequestUtil.reportCallEvent(channel, RtcEvent(RtcEvent.Event.joinSuccess))
LogUtil.e("[agora]$uid 用户声网加入频道成功:channel=$channel") LogUtil.e("[agora]$uid 用户声网加入频道成功:channel=$channel")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
...@@ -383,6 +406,14 @@ class AudioHomeActivity : ...@@ -383,6 +406,14 @@ class AudioHomeActivity :
// } // }
} }
override fun onFirstLocalAudioFrame(elapsed: Int) {
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.localFirstFrame))
}
override fun onFirstRemoteAudioFrame(uid: Int, elapsed: Int) {
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.remoteFirstFrame))
}
/** /**
* https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler.html#a31b2974a574ec45e62bb768e17d1f49e * https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler.html#a31b2974a574ec45e62bb768e17d1f49e
* */ * */
...@@ -390,7 +421,12 @@ class AudioHomeActivity : ...@@ -390,7 +421,12 @@ class AudioHomeActivity :
super.onConnectionStateChanged(state, reason) super.onConnectionStateChanged(state, reason)
// 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑 // 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑
Apm.reportEvent("agora_android", "rtc_connection_failure", "$state,$reason") Apm.reportEvent("agora_android", "rtc_connection_failure", "$state,$reason")
if (reason == 3) { AudioApiRequestUtil.reportCallEvent(
channelId,
RtcEvent(RtcEvent.Event.connectionChanged),
errorCode = state, errorReason = reason.toString()
)
if (reason == 3) {//网络连接被服务器禁止
callEventSave("50", "通话结束:网络连接被服务器中止 该情况现在是因为后端踢人逻辑,原因(${reason}") callEventSave("50", "通话结束:网络连接被服务器中止 该情况现在是因为后端踢人逻辑,原因(${reason}")
writeAgoraLog("通话结束:网络连接被服务器中止 该情况现在是因为后端踢人逻辑,原因(${reason})") writeAgoraLog("通话结束:网络连接被服务器中止 该情况现在是因为后端踢人逻辑,原因(${reason})")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
...@@ -401,8 +437,7 @@ class AudioHomeActivity : ...@@ -401,8 +437,7 @@ class AudioHomeActivity :
// com.yidianling.common.tools.ToastUtil.toastShort("专家已挂断") // com.yidianling.common.tools.ToastUtil.toastShort("专家已挂断")
//通话结束或挂断时,上传日志文件 //通话结束或挂断时,上传日志文件
uploadLog() uploadLog()
leaveChannel() leaveChannel(10)
YDLavManager.instances.callEndStatusUpdate(channelId!!, 4, "服务端踢人触发的回调")
} }
} }
...@@ -423,13 +458,14 @@ class AudioHomeActivity : ...@@ -423,13 +458,14 @@ class AudioHomeActivity :
callEventSave("20", "${uid}加入频道回调") callEventSave("20", "${uid}加入频道回调")
LogUtil.e("[agora]远端用户/主播加入频道回调") LogUtil.e("[agora]远端用户/主播加入频道回调")
onPeerJoined() onPeerJoined()
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.remoteJoin))
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "远端用户/主播加入频道回调 channelId:${channelId}") .sendRichLog(AliYunLogConfig.AGORA, "远端用户/主播加入频道回调 channelId:${channelId}")
} }
override fun onUserOffline(uid: Int, elapsed: Int) { override fun onUserOffline(uid: Int, reason: Int) {
super.onUserOffline(uid, elapsed) super.onUserOffline(uid, reason)
callEventSave("51", "uid:${uid}离开频道回调 elapsed:${elapsed}") callEventSave("51", "uid:${uid}离开频道回调 elapsed:${reason}")
LogUtil.e("[agora]远端用户$uid 离开频道回调") LogUtil.e("[agora]远端用户$uid 离开频道回调")
writeAgoraLog("接通后通话结束:对方已挂断") writeAgoraLog("接通后通话结束:对方已挂断")
...@@ -437,10 +473,13 @@ class AudioHomeActivity : ...@@ -437,10 +473,13 @@ class AudioHomeActivity :
.sendRichLog(AliYunLogConfig.AGORA, "接通后通话结束:对方已挂断 channelId:${channelId}") .sendRichLog(AliYunLogConfig.AGORA, "接通后通话结束:对方已挂断 channelId:${channelId}")
//通话结束或挂断时,上传日志文件 //通话结束或挂断时,上传日志文件
uploadLog() uploadLog()
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.remoteLeave), retCode = reason)
showToast("专家已挂断") showToast("专家已挂断")
//UserOffLine之后,销毁界面,解决,userOffline有回调之后,onConnectionStateChanged(服务端踢人方法没有调),导致记录时长异常。 //UserOffLine之后,销毁界面,解决,userOffline有回调之后,onConnectionStateChanged(服务端踢人方法没有调),导致记录时长异常。
leaveChannel() if (reason == Constants.USER_OFFLINE_DROPPED) {
YDLavManager.instances.callEndStatusUpdate(channelId!!, 4, "对方离开频道") YDLavManager.instances.callEndStatusUpdate(channelId, 3, "对方超时掉线")
}
leaveChannel(if (reason == Constants.USER_OFFLINE_DROPPED) 3 else 4)
if (totalDisposable != null) { if (totalDisposable != null) {
totalDisposable?.dispose() totalDisposable?.dispose()
...@@ -517,6 +556,7 @@ class AudioHomeActivity : ...@@ -517,6 +556,7 @@ class AudioHomeActivity :
return R.layout.audioim_activity_audio_home return R.layout.audioim_activity_audio_home
} }
@SuppressLint("CheckResult")
override fun initDataAndEvent() { override fun initDataAndEvent() {
//状态栏颜色 //状态栏颜色
setWindowStatusBarColor() setWindowStatusBarColor()
...@@ -544,6 +584,22 @@ class AudioHomeActivity : ...@@ -544,6 +584,22 @@ class AudioHomeActivity :
requestPermission() requestPermission()
findRouteService(IImService::class.java).registerObserveCustomNotification(notificationCallback) findRouteService(IImService::class.java).registerObserveCustomNotification(notificationCallback)
Single.create<Int> {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val callMax = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)
val call = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)
val callPercent = (100F * call / callMax).toInt()
it.onSuccess(callPercent)
}
.subscribeOn(Schedulers.io())
.subscribe(Consumer {
val req = hashMapOf(
"deviceId" to DeviceIDHelper.getInstance().deviceId,
"voice" to it
)
YDLHttpUtils.obtainApi(AudioNetAPi::class.java).report(req).subscribe()
})
ActionCountUtils.record("call_phone_page", "call_phone_page_visit", listenerUid ?: "0", "1") ActionCountUtils.record("call_phone_page", "call_phone_page_visit", listenerUid ?: "0", "1")
} }
...@@ -748,12 +804,12 @@ class AudioHomeActivity : ...@@ -748,12 +804,12 @@ class AudioHomeActivity :
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "已接通:主叫主动挂断 channelId:${channelId}") .sendRichLog(AliYunLogConfig.AGORA, "已接通:主叫主动挂断 channelId:${channelId}")
updateExpertStatus(false, 1) updateExpertStatus(false, 1)
leaveChannel() leaveChannel(0)
uploadLog() uploadLog()
YDLavManager.instances.callEndStatusUpdate(channelId!!, 3, "主叫主动挂断") YDLavManager.instances.callEndStatusUpdate(channelId!!, 51, "主叫挂断")
callEventSave("51", "已接通:主叫主动挂断") callEventSave("51", "已接通:主叫主动挂断")
} else { } else {
userCloseCalling() userCloseCalling(0)
} }
ActionCountUtils.count( ActionCountUtils.count(
...@@ -1040,7 +1096,7 @@ class AudioHomeActivity : ...@@ -1040,7 +1096,7 @@ class AudioHomeActivity :
uploadLog() uploadLog()
showToast("暂时无法接通,建议稍后尝试") showToast("暂时无法接通,建议稍后尝试")
userCloseCalling() userCloseCalling(6)
} }
} }
...@@ -1062,26 +1118,50 @@ class AudioHomeActivity : ...@@ -1062,26 +1118,50 @@ class AudioHomeActivity :
* param appId 应用id * param appId 应用id
* param mRtcEventHandler 事件回调(SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等) * param mRtcEventHandler 事件回调(SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等)
*/ */
voiceManage = YDLVoiceManager(this, BuildConfig.AGORA_APPID, mRtcEventHandler) voiceManage = YDLVoiceManager()
voiceManage?.init() voiceManage?.attachVoiceEventHandler(mRtcEventHandler)
voiceManage?.getVoiceApi()?.setAudioProfile(Constants.AUDIO_PROFILE_DEFAULT,Constants.AUDIO_SCENARIO_CHATROOM_GAMING) voiceManage?.getVoiceApi()?.setAudioProfile(Constants.AUDIO_PROFILE_DEFAULT, Constants.AUDIO_SCENARIO_CHATROOM_GAMING)
voiceManage?.attachNetQualityListener(
NetQuality(
this,
channelId,
voiceManage,
tvNetUp,
ivNetQuality,
tvNetQuality,
tvNetDown,
tvNetDelay,
layoutQualityGroup
)
)
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.initSdk))
} }
/** /**
* 声网加入频道 * 声网加入频道
*/ */
fun joinChannel() { fun joinChannel() {
if (hasJoinChannel) return
callEventSave("20", "对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId") callEventSave("20", "对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId")
writeAgoraLog("对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId") writeAgoraLog("对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId") .sendRichLog(AliYunLogConfig.AGORA, "对方(专家)接受了通话邀请,主叫(用户)开始加入频道:$channelId")
voiceManage?.getVoiceApi()?.joinChannel( AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.join))
val joinCode = voiceManage?.getVoiceApi()?.joinChannel(
token!!, token!!,
channelId!!, channelId!!,
"Extra Optional Data", "Extra Optional Data",
YdlCommonRouterManager.getYdlCommonRoute().getUid() YdlCommonRouterManager.getYdlCommonRoute().getUid()
) )
if (joinCode != null && joinCode == 0) {
hasJoinChannel = true
}
if (joinCode != null && joinCode < 0) {
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.joinFail), retCode = joinCode)
}
} }
/** /**
...@@ -1089,7 +1169,7 @@ class AudioHomeActivity : ...@@ -1089,7 +1169,7 @@ class AudioHomeActivity :
* 注意:这个方法只在专家还未接听的状态才能调用 * 注意:这个方法只在专家还未接听的状态才能调用
* *
*/ */
private fun userCloseCalling() { private fun userCloseCalling(reason: Int) {
callEventSave("32", "未接听时:主叫(用户)主动挂断,取消呼叫") callEventSave("32", "未接听时:主叫(用户)主动挂断,取消呼叫")
writeAgoraLog("未接听时:主叫(用户)主动挂断,取消呼叫") writeAgoraLog("未接听时:主叫(用户)主动挂断,取消呼叫")
...@@ -1112,7 +1192,7 @@ class AudioHomeActivity : ...@@ -1112,7 +1192,7 @@ class AudioHomeActivity :
uploadLog() uploadLog()
handler?.postDelayed({ handler?.postDelayed({
LogUtil.e("离开频道") LogUtil.e("离开频道")
leaveChannel() leaveChannel(reason)
}, 100) }, 100)
} }
...@@ -1150,7 +1230,7 @@ class AudioHomeActivity : ...@@ -1150,7 +1230,7 @@ class AudioHomeActivity :
// 如果声网未连接成功,切换axb的弹框是自动弹出的,当关闭弹框的时候,执行用户挂断操作 // 如果声网未连接成功,切换axb的弹框是自动弹出的,当关闭弹框的时候,执行用户挂断操作
// 如果声网连接成功,点击右上角按钮弹出切换axb弹框,但是关闭时用户不执行挂断操作 // 如果声网连接成功,点击右上角按钮弹出切换axb弹框,但是关闭时用户不执行挂断操作
if (!isConnectSuccess) { if (!isConnectSuccess) {
userCloseCalling() userCloseCalling(-1)
} }
} }
}) })
...@@ -1173,7 +1253,7 @@ class AudioHomeActivity : ...@@ -1173,7 +1253,7 @@ class AudioHomeActivity :
} }
}) })
YDLavManager.instances.callEndStatusUpdate(channelId!!, 3, "接通中:主叫主动切换AXB") YDLavManager.instances.callEndStatusUpdate(channelId!!, 51, "接通中:主叫主动切换AXB")
} else { } else {
mPresenter.getAXBPhone(ConnectCommand(listenId!!, "1")) mPresenter.getAXBPhone(ConnectCommand(listenId!!, "1"))
...@@ -1207,7 +1287,7 @@ class AudioHomeActivity : ...@@ -1207,7 +1287,7 @@ class AudioHomeActivity :
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "未接听时:主叫主动挂断失败,msg=$msg($code),再次挂断 channelId:${channelId}") .sendRichLog(AliYunLogConfig.AGORA, "未接听时:主叫主动挂断失败,msg=$msg($code),再次挂断 channelId:${channelId}")
} }
leaveChannel() leaveChannel(5)
} }
} }
...@@ -1266,7 +1346,7 @@ class AudioHomeActivity : ...@@ -1266,7 +1346,7 @@ class AudioHomeActivity :
if (mPlayer == null) { if (mPlayer == null) {
mPlayer = AudioPlayer(this) mPlayer = AudioPlayer(this)
} }
mPlayer?.setDataSource(R.raw.audioim_hand_down_music) mPlayer?.setDataSource(R.raw.effect_hand_up)
mPlayer?.switchPlayType(iv_hands_free.isSelected) mPlayer?.switchPlayType(iv_hands_free.isSelected)
mPlayer?.setCompletionListener(MediaPlayer.OnCompletionListener { mPlayer?.setCompletionListener(MediaPlayer.OnCompletionListener {
LogUtil.e("播放结束") LogUtil.e("播放结束")
...@@ -1360,10 +1440,10 @@ class AudioHomeActivity : ...@@ -1360,10 +1440,10 @@ class AudioHomeActivity :
}, { }, {
LogUtil.d(it.message) LogUtil.d(it.message)
}, { }, {
leaveChannel() leaveChannel(7)
uploadLog() uploadLog()
callEventSave("50", "倾诉时间已用完") callEventSave("50", "倾诉时间已用完")
YDLavManager.instances.callEndStatusUpdate(channelId!!, 3, "倾诉时间已用完") YDLavManager.instances.callEndStatusUpdate(channelId!!, 50, "倾诉时间已用完")
//注意:自动挂断时,如果对方离开频道的回调已经触发,就不要再重复调用接口 //注意:自动挂断时,如果对方离开频道的回调已经触发,就不要再重复调用接口
showToast("通话已结束") showToast("通话已结束")
//通话结束或挂断时,上传日志文件 //通话结束或挂断时,上传日志文件
...@@ -1438,8 +1518,10 @@ class AudioHomeActivity : ...@@ -1438,8 +1518,10 @@ class AudioHomeActivity :
/** /**
* 声网离开频道 * 声网离开频道
* 未知-1拒绝1网络连接被服务器禁止2对方掉线或离开3主动挂断或取消4被叫接听超时7转AXB8倾诉时间用完9
* 出异常10,加入通道超时11,声网Error12,页面销毁13
*/ */
fun leaveChannel() { fun leaveChannel(reason: Int) {
LogUtil.e("调用leaveChannel方法,isLeavelChannel=$isLeavelChannel") LogUtil.e("调用leaveChannel方法,isLeavelChannel=$isLeavelChannel")
if (!isLeavelChannel) { if (!isLeavelChannel) {
isLeavelChannel = true isLeavelChannel = true
...@@ -1451,6 +1533,12 @@ class AudioHomeActivity : ...@@ -1451,6 +1533,12 @@ class AudioHomeActivity :
//播放结束音频 //播放结束音频
playFinishMusic() playFinishMusic()
// 声网离开房间 // 声网离开房间
if (reason in 0..11) {
val from = LeaveChannelReason.getFrom(reason)
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.leave), retCode = from, errorCode = reason)
} else {
XLog.e("audiohomeActivity", "leaveChannel$reason")
}
voiceManage?.getVoiceApi()?.leaveChannel() voiceManage?.getVoiceApi()?.leaveChannel()
} }
} }
...@@ -1504,7 +1592,7 @@ class AudioHomeActivity : ...@@ -1504,7 +1592,7 @@ class AudioHomeActivity :
/** /**
* 网络状态 * 网络状态
*/ */
private fun showNetStatus(msg: String, status: Int = 0) { fun showNetStatus(msg: String, status: Int = 0) {
LogUtil.e("showNetStatus: msg=$msg,status=$status") LogUtil.e("showNetStatus: msg=$msg,status=$status")
if (tv_nte_status.visibility == View.VISIBLE) { if (tv_nte_status.visibility == View.VISIBLE) {
return return
...@@ -1640,7 +1728,7 @@ class AudioHomeActivity : ...@@ -1640,7 +1728,7 @@ class AudioHomeActivity :
// 声网离开房间 // 声网离开房间
voiceManage?.getVoiceApi()?.leaveChannel() voiceManage?.getVoiceApi()?.leaveChannel()
} }
voiceManage?.getVoiceApi()?.destroy() // voiceManage?.getVoiceApi()?.destroy()
voiceManage = null voiceManage = null
} }
......
package com.ydl.audioim
import android.app.Activity
import android.content.Context
import android.text.TextUtils
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.tencent.mmkv.MMKV
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.RtcEvent
import com.ydl.consultantim.ConsultantAudioHomeActivity
import com.ydl.ydl_av.voice.listener.RtcNetInterface
import com.ydl.ydl_av.voice.manager.YDLVoiceManager
import com.ydl.ydlcommon.utils.log.XLog
import io.agora.rtc.Constants
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import java.io.File
import java.io.FileOutputStream
class NetQuality(
val activity: Activity,
val channelId: String?,
val voiceManage: YDLVoiceManager?,
val tvNetUp: TextView,
val ivNetQuality: ImageView,
val tvNetQuality: TextView,
val tvNetDown: TextView,
val tvNetDelay: TextView,
val layoutQualityGroup: View
) : RtcNetInterface {
private var lastOnNetQualityDownReport = 0L
private var lastOnNetQualityUpReport = 0L
private var lastOnRemoteNetworkCongestion = 0L
override fun onLocalAudioStats(bitrate: Int, packetLossRate: Int) {
XLog.i("NetQuality", "bitrate:$bitrate,packetLossRate:$packetLossRate")
activity.runOnUiThread {
tvNetUp.text = "${bitrate}Kbps"
}
}
override fun onNetQuality(uid: Int, tx: Int, rx: Int) {
if (tx in 4..6 || rx in 4..6) {
XLog.i("NetQuality", "$uid,tx:$tx,rx:$rx")
}
activity.runOnUiThread {
if (uid == 0) {
when (rx) {
1 -> {
ivNetQuality.setImageResource(R.drawable.audioim_bg_net_excellent)
tvNetQuality.text = "网络优秀"
}
2, 3 -> {
ivNetQuality.setImageResource(R.drawable.audioim_bg_net_good)
tvNetQuality.text = "网络良好"
}
4, 5, 6 -> {
ivNetQuality.setImageResource(R.drawable.audioim_bg_net_poor)
tvNetQuality.text = "网络极差"
if (System.currentTimeMillis() - lastOnNetQualityDownReport > 10 * 1000) {
lastOnNetQualityDownReport = System.currentTimeMillis()
AudioApiRequestUtil.reportCallEvent(
channelId,
RtcEvent(RtcEvent.Event.networkDownPoor),
retCode = rx
)
}
}
}
if (tx in 4..6) {
if (System.currentTimeMillis() - lastOnNetQualityUpReport > 10 * 1000) {
lastOnNetQualityUpReport = System.currentTimeMillis()
AudioApiRequestUtil.reportCallEvent(
channelId,
RtcEvent(RtcEvent.Event.networkUpPoor),
retCode = rx
)
}
}
}
}
}
override fun onNetworkTypeChanged(type: Int) {
AudioApiRequestUtil.reportCallEvent(channelId, RtcEvent(RtcEvent.Event.networkTypeChanged), retCode = type)
}
override fun onRemoteAudioFeel(frozenRate: Int, qoeQuality: Int, reason: Int, mos: Int) {
XLog.i("NetQuality", "frozenRate:$frozenRate,qoeQuality:$qoeQuality,reason:$reason,mos:$mos")
}
override fun onRemoteAudioStateChanged(uid: Int, state: Int, reason: Int, elapsed: Int) {
XLog.i("NetQuality", "uid:$uid,state:$state,reason:$reason,elapsed:$elapsed")
if (reason == Constants.REMOTE_AUDIO_REASON_NETWORK_CONGESTION && state == Constants.REMOTE_AUDIO_STATE_FROZEN) {
val qualityValue = MMKV.defaultMMKV().getString("network_quality_config", "0")
if (TextUtils.equals(qualityValue, "1")) {
if (System.currentTimeMillis() - lastOnRemoteNetworkCongestion > 30 * 1000) {
lastOnRemoteNetworkCongestion = System.currentTimeMillis()
voiceManage?.getVoiceApi()
?.playEffect(3001, getNetLowEffect(activity))
}
if (activity is AudioHomeActivity) {
activity.showNetStatus("对方的网络状况不佳", 0)
} else if (activity is ConsultantAudioHomeActivity) {
activity.showNetStatus("对方的网络状况不佳", 0)
}
}
}
}
override fun onRemoteAudioStats(quality: Int, delay: Int, bitrate: Int, audioLossRate: Int) {
XLog.i("NetQuality", "quality:$quality,delay:$delay,bitrate:$bitrate,audioLossRate:$audioLossRate")
activity.runOnUiThread {
tvNetDown.text = "${bitrate}Kbps"
tvNetDelay.text = "${delay}ms"
layoutQualityGroup.visibility = View.VISIBLE
}
}
companion object {
fun getHandUpEffect(context: Context): String {
return context.filesDir.absolutePath + "/sounds/effect_hand_up.mp3"
}
fun getNetLowEffect(context: Context): String {
return context.filesDir.absolutePath + "/sounds/effect_net_quality_low.m4a"
}
fun copySoundFile(context: Context?) {
if (context == null) return
Single.create<Int> {
val filesDir = context.filesDir
val soundFile = File(filesDir, "sounds")
if (!soundFile.exists()) {
soundFile.mkdir()
}
val filesName = arrayOf("effect_hand_up.mp3", "effect_net_quality_low.m4a")
filesName.forEach {
val file = File(soundFile, it)
if (!file.exists()) {
file.createNewFile()
val fileOutputStream = FileOutputStream(file)
context.assets.open(it).copyTo(fileOutputStream)
fileOutputStream.close()
}
}
}
.observeOn(Schedulers.io())
.subscribe()
}
}
}
\ No newline at end of file
...@@ -9,6 +9,7 @@ import com.apm.insight.log.VLog ...@@ -9,6 +9,7 @@ import com.apm.insight.log.VLog
import com.google.gson.Gson import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraInvitationBean import com.ydl.audioim.bean.AgoraInvitationBean
import com.ydl.audioim.http.AudioApiRequestUtil import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.IMEvent
import com.ydl.audioim.http.command.ConnectExceptionCommand import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.router.AudioImIn import com.ydl.audioim.router.AudioImIn
import com.ydl.audioim.utils.AudioLogUtils import com.ydl.audioim.utils.AudioLogUtils
...@@ -24,6 +25,7 @@ import com.ydl.ydl_av.messge_service.callback.LoginCallback ...@@ -24,6 +25,7 @@ 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.request.LoginParam
import com.ydl.ydl_av.messge_service.response.CallLocalResponse import com.ydl.ydl_av.messge_service.response.CallLocalResponse
import com.ydl.ydl_av.messge_service.response.CallRemoteResponse import com.ydl.ydl_av.messge_service.response.CallRemoteResponse
import com.ydl.ydl_av.voice.manager.YDLVoiceManager
import com.ydl.ydlcommon.app.Apm import com.ydl.ydlcommon.app.Apm
import com.ydl.ydlcommon.modular.ModularServiceManager import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.utils.ActivityManager import com.ydl.ydlcommon.utils.ActivityManager
...@@ -32,7 +34,6 @@ import com.ydl.ydlcommon.utils.log.AliYunLogConfig ...@@ -32,7 +34,6 @@ import com.ydl.ydlcommon.utils.log.AliYunLogConfig
import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper
import com.ydl.ydlcommon.utils.log.LogHelper import com.ydl.ydlcommon.utils.log.LogHelper
import com.yidianling.im.api.bean.IMSendCustomNotificationResultCallBack import com.yidianling.im.api.bean.IMSendCustomNotificationResultCallBack
import com.yidianling.user.api.event.UserLoginEvent
import com.yidianling.user.api.event.UserLogoutEvent import com.yidianling.user.api.event.UserLogoutEvent
import com.yidianling.user.api.service.IUserService import com.yidianling.user.api.service.IUserService
import de.greenrobot.event.EventBus import de.greenrobot.event.EventBus
...@@ -63,13 +64,16 @@ class YDLavManager { ...@@ -63,13 +64,16 @@ class YDLavManager {
YDLavManager() YDLavManager()
} }
const val AUDIO_NO_AUTH_ERROR_CODE = "97"//音频权限未通过错误码 const val AUDIO_NO_AUTH_ERROR_CODE = "97"//音频权限未通过错误码
var currentChannelId: String? = null
} }
fun init(context: Context, appId: String) { fun init(context: Context, appId: String) {
YDLRTMClient.instances.init(context, appId, listener) YDLRTMClient.instances.init(context, appId, listener)
EventBus.getDefault().unregister(this) if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
}
//设置回调 //设置回调
setCallback() setCallback()
...@@ -82,10 +86,6 @@ class YDLavManager { ...@@ -82,10 +86,6 @@ class YDLavManager {
} }
} }
public fun onEvent(event: UserLoginEvent) {
instances.login(event.uid)
}
public fun onEvent(event: UserLogoutEvent) { public fun onEvent(event: UserLogoutEvent) {
instances.logout() instances.logout()
} }
...@@ -99,6 +99,7 @@ class YDLavManager { ...@@ -99,6 +99,7 @@ class YDLavManager {
AliYunLogConfig.AGORA, AliYunLogConfig.AGORA,
"${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}" "${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}"
) )
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerReceived))
val dimension = hashMapOf("call" to "call_received_by_peer") val dimension = hashMapOf("call" to "call_received_by_peer")
onConfideEvent(dimension, response?.ChannelId) onConfideEvent(dimension, response?.ChannelId)
...@@ -118,6 +119,8 @@ class YDLavManager { ...@@ -118,6 +119,8 @@ class YDLavManager {
AliYunLogConfig.AGORA, AliYunLogConfig.AGORA,
"${response?.calleeId}已接收呼叫邀请" "${response?.calleeId}已接收呼叫邀请"
) )
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerAccepted))
currentChannelId = response?.ChannelId
//加入声网频道时机修改:主叫收到被叫接受邀请的回调后再加入声网频道 //加入声网频道时机修改:主叫收到被叫接受邀请的回调后再加入声网频道
val act = ActivityManager.getInstance().getTopTaskActivity() val act = ActivityManager.getInstance().getTopTaskActivity()
if (act is AudioHomeActivity) { if (act is AudioHomeActivity) {
...@@ -135,6 +138,7 @@ class YDLavManager { ...@@ -135,6 +138,7 @@ class YDLavManager {
AliYunLogConfig.AGORA, AliYunLogConfig.AGORA,
"${response?.calleeId}已拒绝呼叫邀请" "${response?.calleeId}已拒绝呼叫邀请"
) )
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerRejected))
val dimension = hashMapOf("call" to "call_refused") val dimension = hashMapOf("call" to "call_refused")
onConfideEvent(dimension, response?.ChannelId) onConfideEvent(dimension, response?.ChannelId)
val act = ActivityManager.getInstance().getTopTaskActivity() val act = ActivityManager.getInstance().getTopTaskActivity()
...@@ -161,13 +165,19 @@ class YDLavManager { ...@@ -161,13 +165,19 @@ class YDLavManager {
} }
} }
override fun onCallFailure(response: CallLocalResponse?, errorCode: Int) { override fun onCallFailure(response: CallLocalResponse?, errorCode: Int, errorMsg:String?) {
//返回给主叫 //返回给主叫
LogUtil.e("[agora]呼叫${response?.calleeId}用户失败:${response?.response}") LogUtil.e("[agora]呼叫${response?.calleeId}用户失败:${response?.response}")
AliYunRichLogsHelper.getInstance().sendRichLog( AliYunRichLogsHelper.getInstance().sendRichLog(
AliYunLogConfig.AGORA, AliYunLogConfig.AGORA,
"呼叫${response?.calleeId}用户失败:${response?.response},${errorCode}" "呼叫${response?.calleeId}用户失败:${response?.response},${errorCode}"
) )
AudioApiRequestUtil.reportCallEvent(
response?.ChannelId,
IMEvent(IMEvent.Event.callFail),
errorCode = errorCode,
errorReason = errorMsg
)
val dimension = hashMapOf( val dimension = hashMapOf(
"call" to "call_fail", "call" to "call_fail",
"call_fail" to "code${errorCode}" "call_fail" to "code${errorCode}"
...@@ -275,44 +285,20 @@ class YDLavManager { ...@@ -275,44 +285,20 @@ class YDLavManager {
"呼叫邀请被取消:错误原因(${errorCode})" "呼叫邀请被取消:错误原因(${errorCode})"
) )
} }
callEndStatusUpdate(response?.ChannelId!!, 2, "超时未接听导致的取消呼叫") callEndStatusUpdate(response?.ChannelId!!, 8, "超时未接听导致的取消呼叫")
//关闭页面 //关闭页面
closePage() closePage()
} }
override fun onOtherMsg(error: String?) { override fun onCallSuccess(response: CallLocalResponse?) {
LogUtil.e("[agora]其它消息:${error}") AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.callSuccess))
if (error.equals("呼叫发送成功")) {
writeAgoraLog(
"声网发送通话邀请成功-------Time:${AudioLogUtils.format.format(Calendar.getInstance().time)}",
"confide.log",
true
)
AliYunRichLogsHelper.getInstance().sendRichLog(
AliYunLogConfig.AGORA,
"声网发送通话邀请成功"
)
} else {
writeAgoraLog(
"声网发送通话邀请失败${error}-------Time:${
AudioLogUtils.format.format(
Calendar.getInstance().time
)
}", "confide.log", true
)
LogHelper.getInstance().uploadLog(false)
AliYunRichLogsHelper.getInstance().sendRichLog(
AliYunLogConfig.AGORA,
"声网发送通话邀请失败${error}"
)
}
} }
}) })
} }
fun rtcCall(listenerUid: String?, channelId: String?, sendDoctocrMsg: String?) { fun rtcCall(listenerUid: String?, channelId: String?, sendDoctocrMsg: String?) {
YDLRTMClient.instances.call(listenerUid, channelId, sendDoctocrMsg) YDLRTMClient.instances.call(listenerUid, channelId, sendDoctocrMsg)
AudioApiRequestUtil.reportCallEvent(channelId, IMEvent(IMEvent.Event.startCall))
sendCustomNotification(listenerUid!!, sendDoctocrMsg!!, "1") sendCustomNotification(listenerUid!!, sendDoctocrMsg!!, "1")
} }
...@@ -346,6 +332,7 @@ class YDLavManager { ...@@ -346,6 +332,7 @@ class YDLavManager {
}) })
callEndStatusUpdate(channelId, 1, "主叫取消呼叫") callEndStatusUpdate(channelId, 1, "主叫取消呼叫")
AudioApiRequestUtil.reportCallEvent(channelId, IMEvent(IMEvent.Event.callCancel))
sendCustomNotification(listenerUid, data, "3") sendCustomNotification(listenerUid, data, "3")
} }
...@@ -392,6 +379,9 @@ class YDLavManager { ...@@ -392,6 +379,9 @@ class YDLavManager {
} }
fun login(userId: String?) { fun login(userId: String?) {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this)
}
login(userId) { _, _ -> login(userId) { _, _ ->
} }
} }
...@@ -412,11 +402,13 @@ class YDLavManager { ...@@ -412,11 +402,13 @@ class YDLavManager {
.observeOn(AndroidSchedulers.mainThread()).subscribe({ .observeOn(AndroidSchedulers.mainThread()).subscribe({
if ("200" == it.code) { if ("200" == it.code) {
LogUtil.e("[agora]登录av的login-uid:$userId") LogUtil.e("[agora]登录av的login-uid:$userId")
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.rtmLogin))
YDLRTMClient.instances.login( YDLRTMClient.instances.login(
LoginParam(userId, it.data.token), LoginParam(userId, it.data.token),
object : LoginCallback { object : LoginCallback {
override fun onSuccess() { override fun onSuccess() {
//登陆成功,发起呼叫 //登陆成功,发起呼叫
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.loginSuccess))
LogUtil.e("[agora]实时消息登录成功") LogUtil.e("[agora]实时消息登录成功")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "声网rtm登录成功,uid:$userId") .sendRichLog(AliYunLogConfig.AGORA, "声网rtm登录成功,uid:$userId")
...@@ -433,6 +425,8 @@ class YDLavManager { ...@@ -433,6 +425,8 @@ class YDLavManager {
override fun onFailure(msg: String?) { override fun onFailure(msg: String?) {
if (msg != "LOGIN_ERR_ALREADY_LOGGED_IN") { if (msg != "LOGIN_ERR_ALREADY_LOGGED_IN") {
Apm.reportEvent("rtm_android", "connectionstate_error", msg ?: "") Apm.reportEvent("rtm_android", "connectionstate_error", msg ?: "")
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.loginFail), errorReason = msg)
} }
LogUtil.e("[agora]实时消息登录失败:$msg") LogUtil.e("[agora]实时消息登录失败:$msg")
writeAgoraLog( writeAgoraLog(
...@@ -556,6 +550,7 @@ class YDLavManager { ...@@ -556,6 +550,7 @@ class YDLavManager {
*/ */
private fun logout(isReLogin: Boolean) { private fun logout(isReLogin: Boolean) {
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.rtmLogout))
YDLRTMClient.instances.logout(object : LoginCallback { YDLRTMClient.instances.logout(object : LoginCallback {
override fun onSuccess() { override fun onSuccess() {
//退出登陆成功 //退出登陆成功
...@@ -612,17 +607,11 @@ class YDLavManager { ...@@ -612,17 +607,11 @@ class YDLavManager {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
fun callEndStatusUpdate(channelId: String, endStatus: Int, msg: String) { fun callEndStatusUpdate(channelId: String?, endStatus: Int, msg: String) {
AudioApiRequestUtil.callEndStatusUpdate(channelId, endStatus, msg) if (channelId == null) return
AudioApiRequestUtil.callEventSave(channelId, "7", endStatus.toString(), null, msg)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .subscribe()
.subscribe({
}, {
LogUtil.d("callEndStatusUpdate error: ${it.message}")
AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "callEndStatusUpdate error: ${it.message}")
})
} }
/** /**
...@@ -660,6 +649,12 @@ class YDLavManager { ...@@ -660,6 +649,12 @@ class YDLavManager {
true true
) )
LogUtil.i("[agora]onConnectionStateChanged:state:${state} -->reason:$reason") LogUtil.i("[agora]onConnectionStateChanged:state:${state} -->reason:$reason")
AudioApiRequestUtil.reportCallEvent(
YDLVoiceManager.currentChannel,
IMEvent(IMEvent.Event.onlineStatusChange),
errorCode = state,
errorReason = reason.toString()
)
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "声网rtm登录状态:${state}") .sendRichLog(AliYunLogConfig.AGORA, "声网rtm登录状态:${state}")
/* /*
......
package com.ydl.audioim.bean
data class CallCheckBean(
var callLongTime: Int = 0,
var callTotalTime: Int = 0,
var remainingTime: Int = 0,
var channelId: String? = null,
var headImage: String? = null,
var relationId: String? = null,
var session: String? = null,
var userId: String? = null,
var userName: String? = null
)
\ No newline at end of file
...@@ -4,9 +4,11 @@ package com.ydl.audioim.bean ...@@ -4,9 +4,11 @@ package com.ydl.audioim.bean
* 星链-倾诉日志请求参数 * 星链-倾诉日志请求参数
* */ * */
data class CallEventRequestBody( data class CallEventRequestBody(
var session :String?, var session: String?,
var line :String, var line: String,
var status :String, var status: String,
var response :String, var response: String? = null,
var eventTime :String var eventTime: String,
var remark: String? = null,
var eventType: String? = null
) )
package com.ydl.audioim.http package com.ydl.audioim.http
import androidx.annotation.IntDef
import com.google.gson.Gson import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraTokenResponse import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.CallEventRequestBody import com.ydl.audioim.bean.CallEventRequestBody
...@@ -16,8 +17,10 @@ import com.ydl.ydlcommon.utils.NetworkParamsUtils ...@@ -16,8 +17,10 @@ import com.ydl.ydlcommon.utils.NetworkParamsUtils
import com.ydl.ydlcommon.utils.TimeUtil import com.ydl.ydlcommon.utils.TimeUtil
import com.ydl.ydlnet.YDLHttpUtils import com.ydl.ydlnet.YDLHttpUtils
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.RequestBody import okhttp3.RequestBody
import java.util.*
/** /**
* @author jiucheng * @author jiucheng
...@@ -106,29 +109,151 @@ class AudioApiRequestUtil { ...@@ -106,29 +109,151 @@ class AudioApiRequestUtil {
.listenToken(NetworkParamsUtils.getMaps(cmd)) .listenToken(NetworkParamsUtils.getMaps(cmd))
} }
fun callEndStatusUpdate(
channelId: String,
endStatus: Int,
msg: String
): Observable<BaseAPIResponse<Any>> {
return YDLHttpUtils.obtainApi(AudioNetAPi::class.java)
.callEndStatusUpdate(channelId, endStatus, msg)
}
fun callEventSave( fun callEventSave(
session: String?, session: String?,
line: String, line: String,
status: String, status: String,
response: String response: String?,
remark: String? = null,
eventType: String? = null
): Observable<BaseAPIResponse<Any>> { ): Observable<BaseAPIResponse<Any>> {
var eventTime = TimeUtil.getNowDatetime() val eventTime = TimeUtil.getNowDatetime()
var param = CallEventRequestBody(session, line, status, response, eventTime); val param = CallEventRequestBody(session, line, status, response, eventTime, remark, eventType);
var str = Gson().toJson(param) val str = Gson().toJson(param)
val body = RequestBody.create( val body = RequestBody.create(
MediaType.parse("application/json; charset=utf-8"), MediaType.parse("application/json; charset=utf-8"),
str str
) as RequestBody ) as RequestBody
return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).callEventSave(body) return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).callEventSave(body)
} }
fun reportCallEvent(
session: String?,
event: CallEvent,
retCode: Int? = null,
errorCode: Int? = null,
errorReason: String? = null,
@ReportLevel reportLevel: Int? = null
) {
if (session == null) return
val type = event.eventType.name.lowercase(Locale.getDefault())
val resp = hashMapOf<String, Any>(
"name" to event.name,
"desc" to event.desc,
"reportLevel" to (reportLevel ?: event.reportLevel),
)
if (retCode != null) {
resp["retCode"] = retCode
}
if (errorCode != null) {
resp["errorCode"] = errorCode
}
if (errorReason != null) {
resp["errorReason"] = errorReason
}
val response = Gson().toJson(resp)
callEventSave(session, "7", "20", response, eventType = type).subscribeOn(Schedulers.io()).subscribe()
}
}
}
const val REPORT_LEVEL_INFO = 1
const val REPORT_LEVEL_WARN = 2
const val REPORT_LEVEL_ERROR = 3
enum class EventType {
RTC, IM, LOGIN, DEVICE
}
@IntDef(REPORT_LEVEL_INFO, REPORT_LEVEL_WARN, REPORT_LEVEL_ERROR)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
annotation class ReportLevel
sealed class CallEvent(val eventType: EventType, val name: String, val desc: String, @ReportLevel val reportLevel: Int)
class RtcEvent(val event: Event) :
CallEvent(EventType.RTC, event.name, event.desc, event.reportLevel) {
@Suppress("EnumEntryName")
enum class Event(val desc: String, @ReportLevel val reportLevel: Int) {
initSdk("初始化SDK", REPORT_LEVEL_WARN),
join("本地加入房间", REPORT_LEVEL_WARN),
joinSuccess("加入房间成功", REPORT_LEVEL_INFO),
joinFail("加入房间失败", REPORT_LEVEL_ERROR),
remoteJoin("远端加入房间", REPORT_LEVEL_WARN),
remoteJoinTimeout("远端加入超时", REPORT_LEVEL_WARN),
leave("本地离开房间", REPORT_LEVEL_WARN),
remoteLeave("远端离开房间", REPORT_LEVEL_WARN),
localFirstFrame("本地音频首帧", REPORT_LEVEL_WARN),
remoteFirstFrame("远端音频首帧", REPORT_LEVEL_WARN),
networkTypeChanged("网络变化", REPORT_LEVEL_WARN),
networkDownPoor("网络下行质量差", REPORT_LEVEL_WARN),
networkUpPoor("网络上行质量差", REPORT_LEVEL_WARN),
connectionChanged("连接状态变化", REPORT_LEVEL_WARN),
errorOccurred("错误发生", REPORT_LEVEL_ERROR),
}
}
class IMEvent(event: Event) : CallEvent(EventType.IM, event.name, event.desc, event.reportLevel) {
@Suppress("EnumEntryName")
enum class Event(val desc: String, @ReportLevel val reportLevel: Int) {
rtmLogin("登录RTM", REPORT_LEVEL_INFO),
rtmLogout("登出RTM", REPORT_LEVEL_INFO),
loginSuccess("登录成功", REPORT_LEVEL_INFO),
loginFail("登录失败", REPORT_LEVEL_ERROR),
onlineStatusChange("在线状态变化", REPORT_LEVEL_WARN),
startCall("开始呼叫", REPORT_LEVEL_INFO),
onPeerReceived("到达对方", REPORT_LEVEL_INFO),
onPeerAccepted("对方已接受", REPORT_LEVEL_INFO),
onPeerRejected("对方已拒绝", REPORT_LEVEL_WARN),
callSuccess("呼叫成功", REPORT_LEVEL_INFO),
callFail("呼叫失败", REPORT_LEVEL_ERROR),
callCancel("呼叫取消", REPORT_LEVEL_WARN),
}
}
class LoginEvent(event: Event) :
CallEvent(EventType.LOGIN, event.name, event.desc, event.reportLevel) {
@Suppress("EnumEntryName")
enum class Event(val desc: String, @ReportLevel val reportLevel: Int) {
userLogin("用户登录", REPORT_LEVEL_INFO),
userLogout("用户登出", REPORT_LEVEL_INFO),
userLoginSuccess("登录成功", REPORT_LEVEL_INFO),
userLoginFail("登录失败", REPORT_LEVEL_ERROR),
}
}
class DeviceEvent(event: Event) :
CallEvent(EventType.DEVICE, event.name, event.desc, event.reportLevel) {
@Suppress("EnumEntryName")
enum class Event(val desc: String, @ReportLevel val reportLevel: Int) {
beForeground("回到前台", REPORT_LEVEL_INFO),
beBackground("进入后台", REPORT_LEVEL_WARN),
onTrimMemory("内存使用等级", REPORT_LEVEL_WARN),
onLowMemory("内存低", REPORT_LEVEL_WARN),
}
}
object LeaveChannelReason {
val LeaveChannelReasonNormal = 0
val LeaveChannelReasonRefuse = 1
val LeaveChannelReasonCancel = 2
val LeaveChannelReasonOfflineWithError = 3
val LeaveChannelReasonOfflineNormal = 4
val LeaveChannelReasonTurnToAXB = 5
val LeaveChannelReasonAnswerTimeOut = 6
val LeaveChannelReasonSystemTimeup = 7
val LeaveChannelReasonJoinChannelTimeOut = 8
val LeaveChannelReasonRtcError = 9
val LeaveChannelReasonKickedByServer = 10
val LeaveChannelReasonPageError = 11
fun getFrom(reason: Int): Int {
return when (reason) {
1, 3, 4, 6, 8 -> 2
7, 10 -> 0
else -> 1
}
} }
} }
\ No newline at end of file
package com.ydl.audioim.http package com.ydl.audioim.http
import com.ydl.audioim.bean.AgoraTokenResponse import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.CallCheckBean
import com.ydl.audioim.bean.ConnectBean import com.ydl.audioim.bean.ConnectBean
import com.ydl.consultantim.bean.ListenTokenBean import com.ydl.consultantim.bean.ListenTokenBean
import com.ydl.ydlcommon.base.config.YDL_DOMAIN import com.ydl.ydlcommon.base.config.YDL_DOMAIN
...@@ -19,6 +20,10 @@ import retrofit2.http.* ...@@ -19,6 +20,10 @@ import retrofit2.http.*
* @date 2018/11/9 * @date 2018/11/9
*/ */
interface AudioNetAPi { interface AudioNetAPi {
@GET("counselor/consultationCall/queryCallInfo")
@Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA)
fun checkCall(@Query("calleeUid") calleeUid: String?): Observable<BaseAPIResponse<CallCheckBean>>
/** /**
* 通话开始回调 * 通话开始回调
*/ */
...@@ -33,12 +38,16 @@ interface AudioNetAPi { ...@@ -33,12 +38,16 @@ interface AudioNetAPi {
// @POST("auth/listen-order/callback/agora/finish") // @POST("auth/listen-order/callback/agora/finish")
// fun connectFinish(@Body body:RequestBody): Observable<BaseAPIResponse<Any>> // fun connectFinish(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
@Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA, "Content-Type:application/json")
@POST("counselor/device/upload")
fun report(@Body params: Map<String, @JvmSuppressWildcards Any>): Observable<BaseAPIResponse<String>>
/** /**
* 通话警告与错误回调 * 通话警告与错误回调
*/ */
@Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA,"Content-Type:application/json") @Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA, "Content-Type:application/json")
@POST("auth/listen-order/callback/agora") @POST("auth/listen-order/callback/agora")
fun connectException(@Body body:RequestBody): Observable<BaseAPIResponse<Any>> fun connectException(@Body body: RequestBody): Observable<BaseAPIResponse<Any>>
/** /**
...@@ -69,9 +78,9 @@ interface AudioNetAPi { ...@@ -69,9 +78,9 @@ interface AudioNetAPi {
fun listenToken(@FieldMap map: Map<String, String>): Observable<BaseResponse<ListenTokenBean>> fun listenToken(@FieldMap map: Map<String, String>): Observable<BaseResponse<ListenTokenBean>>
@GET("message/call-end/agora") // @GET("message/call-end/agora")
@Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA) // @Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA)
fun callEndStatusUpdate(@Query("channelId") channelId:String,@Query("endStatus") endStatus:Int,@Query("msg") msg:String): Observable<BaseAPIResponse<Any>> // fun callEndStatusUpdate(@Query("channelId") channelId:String,@Query("endStatus") endStatus:Int,@Query("msg") msg:String): Observable<BaseAPIResponse<Any>>
//倾诉事件上报接口 //倾诉事件上报接口
@Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA, "Content-Type:application/json") @Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA, "Content-Type:application/json")
......
...@@ -5,9 +5,21 @@ import android.content.Context ...@@ -5,9 +5,21 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.google.gson.Gson
import com.ydl.audioim.BuildConfig
import com.ydl.audioim.NetQuality
import com.ydl.audioim.YDLavManager import com.ydl.audioim.YDLavManager
import com.ydl.audioim.api.IAudioImService import com.ydl.audioim.api.IAudioImService
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.REPORT_LEVEL_INFO
import com.ydl.audioim.utils.CallCheck
import com.ydl.audioim.widget.AxbConfirmDialog import com.ydl.audioim.widget.AxbConfirmDialog
import com.ydl.ydl_av.voice.listener.RtcGlobalNet
import com.ydl.ydl_av.voice.manager.YDLVoiceManager
import com.ydl.ydlcommon.modular.findRouteService
import com.ydl.ydlcommon.utils.log.XLog
import com.yidianling.im.api.service.IImService
import io.reactivex.schedulers.Schedulers
/** /**
* Created by Ykai on 2022/7/26. * Created by Ykai on 2022/7/26.
...@@ -33,8 +45,13 @@ class AudioImServiceImp : IAudioImService { ...@@ -33,8 +45,13 @@ class AudioImServiceImp : IAudioImService {
YDLavManager.instances.login(userId) YDLavManager.instances.login(userId)
} }
override fun logoutRtm() {
YDLavManager.instances.logout()
}
override fun openAxbDialog(activity: Activity?, type: Int, phoneNumber: String?) { override fun openAxbDialog(activity: Activity?, type: Int, phoneNumber: String?) {
val dialog = AxbConfirmDialog(activity, type, object : AxbConfirmDialog.OnClickEnsureListener { val dialog =
AxbConfirmDialog(activity, type, object : AxbConfirmDialog.OnClickEnsureListener {
override fun onClickEnsure() { override fun onClickEnsure() {
phoneNumber?.let { phoneNumber?.let {
val phoneIntent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$phoneNumber")) val phoneIntent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$phoneNumber"))
...@@ -45,7 +62,20 @@ class AudioImServiceImp : IAudioImService { ...@@ -45,7 +62,20 @@ class AudioImServiceImp : IAudioImService {
override fun onClose() { override fun onClose() {
} }
}) })
dialog.show()
}
override fun openPermissionRejectDialog(activity: Activity?, callback: () -> Unit) {
val dialog = AxbConfirmDialog(activity, 3, object : AxbConfirmDialog.OnClickEnsureListener {
override fun onClickEnsure() {
callback.invoke()
}
override fun onClose() {
callback.invoke()
}
})
dialog.show() dialog.show()
} }
...@@ -53,7 +83,49 @@ class AudioImServiceImp : IAudioImService { ...@@ -53,7 +83,49 @@ class AudioImServiceImp : IAudioImService {
YDLavManager.instances.callEventSave(status, res, session, line) YDLavManager.instances.callEventSave(status, res, session, line)
} }
override fun initRtcNetQuality() {
YDLVoiceManager.enableLastmileTest(true)
YDLVoiceManager.attachGlobalNetListener(RtcGlobalNetQuality(30))
}
override fun initAgoraRtc(context: Context) {
YDLVoiceManager.init(context.applicationContext, BuildConfig.AGORA_APPID)
findRouteService(IImService::class.java).getSystemConfig("network_quality_config")
}
override fun reportCallEvent(type: String, name: String, desc: String, retCode: Int?) {
val resp = hashMapOf<String, Any>(
"name" to name,
"desc" to desc,
"reportLevel" to REPORT_LEVEL_INFO
)
if (retCode != null) {
resp["retCode"] = retCode
}
val toJson = Gson().toJson(resp)
if (YDLVoiceManager.currentChannel.isNullOrEmpty()) return
AudioApiRequestUtil.callEventSave(YDLVoiceManager.currentChannel, "7", "20", toJson, eventType = type)
.subscribeOn(Schedulers.io())
.subscribe()
}
override fun callCheck(context: Context, calleeUid: String?, delay: Long) {
CallCheck.checkCall(context, calleeUid, delay)
}
override fun init(context: Context?) { override fun init(context: Context?) {
NetQuality.copySoundFile(context)
}
}
internal class RtcGlobalNetQuality(private val threshold: Int) : RtcGlobalNet {
private var count = 0
override fun onLastmileQuality(quality: Int) {
if (quality in 4..6) {
count++
if (quality > 3 && count % 10 == 0) {
XLog.i("RtcGlobalNetQuality", quality.toString())
}
}
} }
} }
\ No newline at end of file
package com.ydl.audioim.utils
import android.content.Context
import android.content.Intent
import com.apm.insight.log.VLog
import com.google.gson.Gson
import com.ydl.audioim.http.AudioNetAPi
import com.ydl.consultantim.ConsultantAudioHomeActivity
import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.ydlnet.YDLHttpUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
object CallCheck {
private var subscribe: Disposable? = null
const val KEY_LATEST_REJECT_CHANNEL = "KeyLatestChannel"
fun checkCall(context: Context, calleeUid: String? = null, delay: Long = 0) {
subscribe?.dispose()
subscribe = Observable.just(1)
.delay(delay, TimeUnit.SECONDS)
.flatMap { YDLHttpUtils.obtainApi(AudioNetAPi::class.java).checkCall(calleeUid) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ resp ->
if (resp.code == "200" && resp.data != null) {
val intent = Intent(context, ConsultantAudioHomeActivity::class.java)
val r = resp.data
val bean = AudioMessageBean(
0, r.channelId, r.userId, r.headImage, r.userName, r.remainingTime, r.callTotalTime,
r.relationId, null, null, null
)
val beanJson = Gson().toJson(bean)
intent.putExtra("param", beanJson)
context.startActivity(intent)
VLog.i("CallCheck", "call check go AudioHomeActivity")
}
}, {
android.util.Log.e("callCheck", it.message)
})
}
}
\ No newline at end of file
...@@ -4,6 +4,7 @@ import android.app.Activity ...@@ -4,6 +4,7 @@ import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
import android.widget.TextView
import com.ydl.audioim.R import com.ydl.audioim.R
import kotlinx.android.synthetic.main.audioim_dialog_axb_confirm.* import kotlinx.android.synthetic.main.audioim_dialog_axb_confirm.*
...@@ -17,7 +18,7 @@ import kotlinx.android.synthetic.main.audioim_dialog_axb_confirm.* ...@@ -17,7 +18,7 @@ import kotlinx.android.synthetic.main.audioim_dialog_axb_confirm.*
class AxbConfirmDialog : Dialog { class AxbConfirmDialog : Dialog {
private var activity: Activity? = null private var activity: Activity? = null
private var listener: OnClickEnsureListener? = null private var listener: OnClickEnsureListener? = null
private var type :Int = 1 //弹窗类型,1为手动切换axb布局,2为自动切换axb布局 private var type :Int = 1 //弹窗类型,1为手动切换axb布局,2为自动切换axb布局,3权限拒绝
constructor(activity: Activity?,type:Int, listener: OnClickEnsureListener?) : super(activity) { constructor(activity: Activity?,type:Int, listener: OnClickEnsureListener?) : super(activity) {
this.activity = activity this.activity = activity
...@@ -29,7 +30,10 @@ class AxbConfirmDialog : Dialog { ...@@ -29,7 +30,10 @@ class AxbConfirmDialog : Dialog {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (this.type==2){ if (this.type==2){
setContentView(R.layout.audioim_dialog_autoaxb_confirm) setContentView(R.layout.audioim_dialog_autoaxb_confirm)
}else{ } else if (this.type == 3) {
setContentView(R.layout.audioim_dialog_autoaxb_confirm)
findViewById<TextView>(R.id.tvTip).text = "您未授权无法使用网络通话\n请点击呼叫使用传统电话"
} else {
setContentView(R.layout.audioim_dialog_axb_confirm) setContentView(R.layout.audioim_dialog_axb_confirm)
} }
......
...@@ -10,30 +10,40 @@ import android.hardware.Sensor ...@@ -10,30 +10,40 @@ import android.hardware.Sensor
import android.hardware.SensorEvent import android.hardware.SensorEvent
import android.hardware.SensorEventListener import android.hardware.SensorEventListener
import android.hardware.SensorManager import android.hardware.SensorManager
import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.text.TextUtils import android.text.TextUtils
import android.view.View import android.view.View
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter import com.alibaba.android.arouter.launcher.ARouter
import com.google.gson.Gson import com.google.gson.Gson
import com.tbruyelle.rxpermissions2.RxPermissions import com.tbruyelle.rxpermissions2.RxPermissions
import com.ydl.audioim.BuildConfig import com.tencent.mmkv.MMKV
import com.ydl.audioim.NetQuality
import com.ydl.audioim.R import com.ydl.audioim.R
import com.ydl.audioim.YDLavManager import com.ydl.audioim.YDLavManager
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.AudioNetAPi
import com.ydl.audioim.http.LeaveChannelReason
import com.ydl.audioim.http.RtcEvent
import com.ydl.audioim.http.command.ConnectExceptionCommand import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.http.command.PayLoad import com.ydl.audioim.http.command.PayLoad
import com.ydl.audioim.player.AudioPlayer import com.ydl.audioim.player.AudioPlayer
import com.ydl.audioim.utils.AudioLogUtils import com.ydl.audioim.utils.AudioLogUtils
import com.ydl.audioim.utils.CallCheck
import com.ydl.audioim.utils.DateUtils import com.ydl.audioim.utils.DateUtils
import com.ydl.consultantim.contract.IConsultantAudioHomeActivityContract import com.ydl.consultantim.contract.IConsultantAudioHomeActivityContract
import com.ydl.consultantim.event.AudioHomeEvent import com.ydl.consultantim.event.AudioHomeEvent
import com.ydl.consultantim.presenter.ConsultantAudioHomePresenterImpl import com.ydl.consultantim.presenter.ConsultantAudioHomePresenterImpl
import com.ydl.consultantim.utils.ConsultantAudioUtils import com.ydl.consultantim.utils.ConsultantAudioUtils
import com.ydl.consultantim.utils.VibratorUtil import com.ydl.consultantim.utils.VibratorUtil
import com.ydl.devicesidlib.DeviceIDHelper
import com.ydl.ydl_av.chat.bean.AudioMessageBean import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.ydl_av.voice.listener.IYDLVoiceEventHandler import com.ydl.ydl_av.voice.listener.IYDLVoiceEventHandler
import com.ydl.ydl_av.voice.manager.YDLVoiceManager import com.ydl.ydl_av.voice.manager.YDLVoiceManager
...@@ -50,14 +60,18 @@ import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils ...@@ -50,14 +60,18 @@ import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.ydl.ydlcommon.utils.log.AliYunLogConfig import com.ydl.ydlcommon.utils.log.AliYunLogConfig
import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper
import com.ydl.ydlcommon.utils.log.LogHelper import com.ydl.ydlcommon.utils.log.LogHelper
import com.ydl.ydlcommon.utils.log.XLog
import com.ydl.ydlcommon.utils.remind.ToastHelper import com.ydl.ydlcommon.utils.remind.ToastHelper
import com.ydl.ydlnet.YDLHttpUtils
import com.yidianling.user.api.service.IUserService import com.yidianling.user.api.service.IUserService
import de.greenrobot.event.EventBus import de.greenrobot.event.EventBus
import io.agora.rtc.Constants import io.agora.rtc.Constants
import io.agora.rtc.IRtcEngineEventHandler import io.agora.rtc.IRtcEngineEventHandler
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.audioim_cativity_consultant_audio_home.* import kotlinx.android.synthetic.main.audioim_cativity_consultant_audio_home.*
import java.util.* import java.util.*
...@@ -119,6 +133,13 @@ class ConsultantAudioHomeActivity : ...@@ -119,6 +133,13 @@ class ConsultantAudioHomeActivity :
*/ */
private var isConnectSuccess: Boolean = false private var isConnectSuccess: Boolean = false
private val tvNetDelay: TextView by lazy { findViewById(R.id.tvDelay) }
private val tvNetDown: TextView by lazy { findViewById(R.id.tvDown) }
private val tvNetUp: TextView by lazy { findViewById(R.id.tvUp) }
private val ivNetQuality: ImageView by lazy { findViewById(R.id.ivQuality) }
private val tvNetQuality: TextView by lazy { findViewById(R.id.tvQuality) }
private val layoutQualityGroup: View by lazy { findViewById(R.id.layoutQualityGroup) }
companion object { companion object {
const val PARAM: String = "param" const val PARAM: String = "param"
...@@ -256,21 +277,13 @@ class ConsultantAudioHomeActivity : ...@@ -256,21 +277,13 @@ class ConsultantAudioHomeActivity :
super.onConnectionStateChanged(state, reason) super.onConnectionStateChanged(state, reason)
// 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑 // 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑
if (reason == 3) { if (reason == 3) {
callEventSave("60", "通话挂断:网络连接被服务器(后端)中止")
showToast("对方已挂断") showToast("对方已挂断")
YDLavManager.instances.callEndStatusUpdate(
mAudioMessageBean?.channelId!!,
4,
"服务端踢人触发的回调"
)
writeAgoraLog("通话挂断:网络连接被服务器(后端)中止") writeAgoraLog("通话挂断:网络连接被服务器(后端)中止")
//通话结束或挂断时,上传日志文件 //通话结束或挂断时,上传日志文件
uploadLog() uploadLog()
// leaveChannel() // leaveChannel()
// finish() // finish()
close(RESULT_ANSWERED_CODE, "") close(RESULT_ANSWERED_CODE, "", 10)
} }
} }
...@@ -319,11 +332,6 @@ class ConsultantAudioHomeActivity : ...@@ -319,11 +332,6 @@ class ConsultantAudioHomeActivity :
} }
} }
runOnUiThread { runOnUiThread {
YDLavManager.instances.callEndStatusUpdate(
mAudioMessageBean?.channelId!!,
4,
"对方离开频道"
)
showToast("对方已挂断") showToast("对方已挂断")
writeAgoraLog("通话接通后挂断:主叫(专家)离开频道") writeAgoraLog("通话接通后挂断:主叫(专家)离开频道")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
...@@ -333,7 +341,7 @@ class ConsultantAudioHomeActivity : ...@@ -333,7 +341,7 @@ class ConsultantAudioHomeActivity :
totalDisposable!!.dispose() totalDisposable!!.dispose()
} }
//通知php 通话已结束 //通知php 通话已结束
close(RESULT_ANSWERED_CODE, "") close(RESULT_ANSWERED_CODE, "", if (elapsed == 1) 3 else 4)
} }
} }
...@@ -427,8 +435,8 @@ class ConsultantAudioHomeActivity : ...@@ -427,8 +435,8 @@ class ConsultantAudioHomeActivity :
} }
YDLavManager.instances.callEndStatusUpdate( YDLavManager.instances.callEndStatusUpdate(
mAudioMessageBean?.channelId!!, mAudioMessageBean?.channelId!!,
4, 60,
"频道的错误回调信息${err} " "频道错误回调${err} "
) )
} }
} }
...@@ -495,6 +503,7 @@ class ConsultantAudioHomeActivity : ...@@ -495,6 +503,7 @@ class ConsultantAudioHomeActivity :
@SuppressLint("CheckResult")
override fun initDataAndEvent() { override fun initDataAndEvent() {
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
callEventSave("30", "进入接听界面") callEventSave("30", "进入接听界面")
...@@ -510,6 +519,21 @@ class ConsultantAudioHomeActivity : ...@@ -510,6 +519,21 @@ class ConsultantAudioHomeActivity :
initData() initData()
//获取声网频道号 //获取声网频道号
getChannelToken() getChannelToken()
Single.create<Int> {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val callMax = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)
val call = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)
val callPercent = (100F * call / callMax).toInt()
it.onSuccess(callPercent)
}
.subscribeOn(Schedulers.io())
.subscribe(Consumer {
val req = hashMapOf(
"deviceId" to DeviceIDHelper.getInstance().deviceId,
"voice" to it
)
YDLHttpUtils.obtainApi(AudioNetAPi::class.java).report(req).subscribe()
})
} }
private fun setWindowStatusBarColor() { private fun setWindowStatusBarColor() {
...@@ -527,6 +551,11 @@ class ConsultantAudioHomeActivity : ...@@ -527,6 +551,11 @@ class ConsultantAudioHomeActivity :
writeAgoraLog("收到邀请通话消息内容:$json") writeAgoraLog("收到邀请通话消息内容:$json")
mAudioMessageBean = Gson().fromJson(json, AudioMessageBean::class.java) mAudioMessageBean = Gson().fromJson(json, AudioMessageBean::class.java)
val rejectChannel = MMKV.defaultMMKV().getString(CallCheck.KEY_LATEST_REJECT_CHANNEL, "")
if(rejectChannel == mAudioMessageBean?.channelId){
finish()
}
if (YDLavManager.sdkStatus != Constants.CONNECTION_STATE_CONNECTED) { if (YDLavManager.sdkStatus != Constants.CONNECTION_STATE_CONNECTED) {
callEventSave("90", "RMT状态:${YDLavManager.sdkStatus},重新登录RMT") callEventSave("90", "RMT状态:${YDLavManager.sdkStatus},重新登录RMT")
...@@ -690,9 +719,23 @@ class ConsultantAudioHomeActivity : ...@@ -690,9 +719,23 @@ class ConsultantAudioHomeActivity :
* param appId 应用id * param appId 应用id
* param mRtcEventHandler 事件回调(SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等) * param mRtcEventHandler 事件回调(SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等)
*/ */
voiceManage = YDLVoiceManager(this, BuildConfig.AGORA_APPID, mRtcEventHandler) voiceManage = YDLVoiceManager()
voiceManage?.init() voiceManage?.attachVoiceEventHandler(mRtcEventHandler)
voiceManage?.getVoiceApi()?.setAudioProfile(Constants.AUDIO_PROFILE_DEFAULT,Constants.AUDIO_SCENARIO_CHATROOM_GAMING) voiceManage?.attachNetQualityListener(
NetQuality(
this,
mAudioMessageBean?.channelId,
voiceManage,
tvNetUp,
ivNetQuality,
tvNetQuality,
tvNetDown,
tvNetDelay,
layoutQualityGroup
)
)
voiceManage?.getVoiceApi()?.setAudioProfile(Constants.AUDIO_PROFILE_DEFAULT, Constants.AUDIO_SCENARIO_CHATROOM_GAMING)
AudioApiRequestUtil.reportCallEvent(mAudioMessageBean?.channelId, RtcEvent(RtcEvent.Event.initSdk))
} }
...@@ -708,10 +751,14 @@ class ConsultantAudioHomeActivity : ...@@ -708,10 +751,14 @@ class ConsultantAudioHomeActivity :
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:joinChannel:$account channelId:${mAudioMessageBean?.channelId}") .sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:joinChannel:$account channelId:${mAudioMessageBean?.channelId}")
voiceManage?.getVoiceApi()?.joinChannel( AudioApiRequestUtil.reportCallEvent(mAudioMessageBean?.channelId, RtcEvent(RtcEvent.Event.join))
val joinCode = voiceManage?.getVoiceApi()?.joinChannel(
channelToken channelToken
?: "", mAudioMessageBean!!.channelId!!, "Extra Optional Data", account ?: "", mAudioMessageBean!!.channelId!!, "Extra Optional Data", account
) )
if (joinCode != null && joinCode < 0) {
AudioApiRequestUtil.reportCallEvent(mAudioMessageBean?.channelId, RtcEvent(RtcEvent.Event.joinFail), retCode = joinCode)
}
} }
} }
...@@ -758,10 +805,10 @@ class ConsultantAudioHomeActivity : ...@@ -758,10 +805,10 @@ class ConsultantAudioHomeActivity :
} else if (status == STATUS_ANSWERED) { } else if (status == STATUS_ANSWERED) {
callEventSave("51", "通话接通后挂断:专家主动挂断") callEventSave("51", "通话接通后挂断:专家主动挂断")
YDLavManager.instances.callEndStatusUpdate(mAudioMessageBean?.channelId!!, 3, "被叫主动拒绝") YDLavManager.instances.callEndStatusUpdate(mAudioMessageBean?.channelId!!, 2, "被叫主动拒绝")
writeAgoraLog("通话接通后挂断:专家主动挂断") writeAgoraLog("通话接通后挂断:专家主动挂断")
//正常接听 挂断电话 需要重置信令管理类状态 //正常接听 挂断电话 需要重置信令管理类状态
close(RESULT_ANSWERED_CODE, "") close(RESULT_ANSWERED_CODE, "", 0)
} }
} }
...@@ -785,6 +832,9 @@ class ConsultantAudioHomeActivity : ...@@ -785,6 +832,9 @@ class ConsultantAudioHomeActivity :
YdlCommonRouterManager.getYdlCommonRoute().getUid().toString(), YdlCommonRouterManager.getYdlCommonRoute().getUid().toString(),
uid = YdlCommonRouterManager.getYdlCommonRoute().getUid().toString() uid = YdlCommonRouterManager.getYdlCommonRoute().getUid().toString()
) )
if (!mAudioMessageBean?.channelId.isNullOrEmpty()) {
MMKV.defaultMMKV().putString(CallCheck.KEY_LATEST_REJECT_CHANNEL, mAudioMessageBean?.channelId)
}
} }
/** /**
...@@ -909,7 +959,7 @@ class ConsultantAudioHomeActivity : ...@@ -909,7 +959,7 @@ class ConsultantAudioHomeActivity :
/** /**
* 网络状态 * 网络状态
*/ */
private fun showNetStatus(msg: String, status: Int = 0) { fun showNetStatus(msg: String, status: Int = 0) {
runOnUiThread { runOnUiThread {
if (TextUtils.isEmpty(msg)) { if (TextUtils.isEmpty(msg)) {
tv_nte_status.visibility = View.GONE tv_nte_status.visibility = View.GONE
...@@ -940,7 +990,7 @@ class ConsultantAudioHomeActivity : ...@@ -940,7 +990,7 @@ class ConsultantAudioHomeActivity :
} }
//关闭本页面 //关闭本页面
fun close(code: Int, msg: String) { fun close(code: Int, msg: String, reason: Int = -1) {
runOnUiThread { runOnUiThread {
LogUtil.e("[agora]close(code:$code,msg:$msg)") LogUtil.e("[agora]close(code:$code,msg:$msg)")
AliYunRichLogsHelper.getInstance() AliYunRichLogsHelper.getInstance()
...@@ -957,7 +1007,7 @@ class ConsultantAudioHomeActivity : ...@@ -957,7 +1007,7 @@ class ConsultantAudioHomeActivity :
//离开频道 //离开频道
AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:已接听,正常挂断 channelId:${mAudioMessageBean?.channelId}") AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:已接听,正常挂断 channelId:${mAudioMessageBean?.channelId}")
leaveChannel() leaveChannel(reason)
} }
RESULT_NOT_ANSWERED_CODE -> { RESULT_NOT_ANSWERED_CODE -> {
callStatus = 2 callStatus = 2
...@@ -1008,7 +1058,7 @@ class ConsultantAudioHomeActivity : ...@@ -1008,7 +1058,7 @@ class ConsultantAudioHomeActivity :
if (mPlayer == null) { if (mPlayer == null) {
mPlayer = AudioPlayer(this, true) mPlayer = AudioPlayer(this, true)
} }
mPlayer!!.setDataSource(R.raw.audioim_hand_down_music) mPlayer!!.setDataSource(R.raw.effect_hand_up)
// mPlayer!!.switchPlayType(true) // mPlayer!!.switchPlayType(true)
mPlayer!!.start(isLooping = false, isSetOnCompletionListener = false) mPlayer!!.start(isLooping = false, isSetOnCompletionListener = false)
} }
...@@ -1028,9 +1078,10 @@ class ConsultantAudioHomeActivity : ...@@ -1028,9 +1078,10 @@ class ConsultantAudioHomeActivity :
if (!isConnectSuccess) { if (!isConnectSuccess) {
YDLavManager.instances.callEndStatusUpdate( YDLavManager.instances.callEndStatusUpdate(
mAudioMessageBean?.channelId!!, mAudioMessageBean?.channelId!!,
1, 36,
"被叫加入频道后主叫未加入超时" "被叫加入频道后主叫未加入超时"
) )
AudioApiRequestUtil.reportCallEvent(mAudioMessageBean?.channelId, RtcEvent(RtcEvent.Event.remoteJoinTimeout))
AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:15s后如果还是处于连接中,则直接退出当前页面 channelId:${mAudioMessageBean?.channelId}") AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:15s后如果还是处于连接中,则直接退出当前页面 channelId:${mAudioMessageBean?.channelId}")
writeAgoraLog("通话未接通挂断:连接中的状态超过5s自动挂断") writeAgoraLog("通话未接通挂断:连接中的状态超过5s自动挂断")
...@@ -1044,12 +1095,17 @@ class ConsultantAudioHomeActivity : ...@@ -1044,12 +1095,17 @@ class ConsultantAudioHomeActivity :
/** /**
* 离开频道 * 离开频道
*/ */
private fun leaveChannel() { private fun leaveChannel(reason: Int) {
uploadException("", "zhu", "108") uploadException("", "zhu", "108")
totalDisposable?.dispose() totalDisposable?.dispose()
connectingStatusDisposable?.dispose() connectingStatusDisposable?.dispose()
if (reason in 0..11) {
val from = LeaveChannelReason.getFrom(reason)
AudioApiRequestUtil.reportCallEvent(mAudioMessageBean?.channelId, RtcEvent(RtcEvent.Event.leave), retCode = from, errorCode = reason)
} else {
XLog.e("audiohomeActivity", "leaveChannel$reason")
}
voiceManage?.getVoiceApi()?.leaveChannel() voiceManage?.getVoiceApi()?.leaveChannel()
voiceManage?.getVoiceApi()?.destroy()
voiceManage = null voiceManage = null
mPlayer?.clear() mPlayer?.clear()
sensorManager?.unregisterListener(this) sensorManager?.unregisterListener(this)
...@@ -1063,7 +1119,6 @@ class ConsultantAudioHomeActivity : ...@@ -1063,7 +1119,6 @@ class ConsultantAudioHomeActivity :
} }
private fun voiceDestory() { private fun voiceDestory() {
voiceManage?.getVoiceApi()?.destroy()
voiceManage = null voiceManage = null
} }
...@@ -1117,7 +1172,7 @@ class ConsultantAudioHomeActivity : ...@@ -1117,7 +1172,7 @@ class ConsultantAudioHomeActivity :
LogUtil.e("http-------------onDestory") LogUtil.e("http-------------onDestory")
AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:onDestroy channelId:${mAudioMessageBean?.channelId}") AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.AGORA, "咨询用户端:onDestroy channelId:${mAudioMessageBean?.channelId}")
leaveChannel() leaveChannel(11)
voiceDestory() voiceDestory()
isConnectSuccess = false isConnectSuccess = false
status = STATUS_NOT_ANSWERED status = STATUS_NOT_ANSWERED
......
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#48CC95" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FF994B" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#E05252" />
</shape>
\ No newline at end of file
...@@ -38,6 +38,85 @@ ...@@ -38,6 +38,85 @@
android:textSize="13sp" /> android:textSize="13sp" />
</LinearLayout> </LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutQualityGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="44dp"
android:layout_marginRight="18dp"
android:visibility="gone">
<TextView
android:id="@+id/tvDelay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_delay"
android:drawablePadding="4dp"
android:textColor="#99FFFFFF"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tvDown"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<TextView
android:id="@+id/tvDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_down"
android:textColor="#99FFFFFF"
android:drawablePadding="4dp"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/tvDelay"
app:layout_constraintRight_toLeftOf="@+id/tvUp"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<TextView
android:id="@+id/tvUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_up"
android:textColor="#99FFFFFF"
android:drawablePadding="4dp"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/tvDown"
app:layout_constraintRight_toLeftOf="@+id/layoutQuality"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<LinearLayout
android:id="@+id/layoutQuality"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintLeft_toRightOf="@+id/tvUp"
app:layout_constraintRight_toRightOf="parent">
<ImageView
android:id="@+id/ivQuality"
tools:src="@drawable/audioim_bg_net_excellent"
android:layout_width="6dp"
android:layout_height="6dp"/>
<TextView
android:id="@+id/tvQuality"
android:layout_width="wrap_content"
android:layout_marginLeft="4dp"
android:layout_height="wrap_content"
android:textColor="#99FFFFFF"
android:textSize="13sp"
tools:text="12ms" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/rl_head" android:id="@+id/rl_head"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/platform_color_80353535"> android:background="@color/platform_color_80353535">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutQualityGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="44dp"
android:layout_marginRight="18dp"
android:visibility="gone">
<TextView
android:id="@+id/tvDelay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_delay"
android:drawablePadding="4dp"
android:textColor="#99FFFFFF"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tvDown"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<TextView
android:id="@+id/tvDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_down"
android:textColor="#99FFFFFF"
android:drawablePadding="4dp"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/tvDelay"
app:layout_constraintRight_toLeftOf="@+id/tvUp"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<TextView
android:id="@+id/tvUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_net_up"
android:textColor="#99FFFFFF"
android:drawablePadding="4dp"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/tvDown"
app:layout_constraintRight_toLeftOf="@+id/layoutQuality"
app:layout_constraintTop_toTopOf="parent"
tools:text="12ms" />
<LinearLayout
android:id="@+id/layoutQuality"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintLeft_toRightOf="@+id/tvUp"
app:layout_constraintRight_toRightOf="parent">
<ImageView
android:id="@+id/ivQuality"
tools:src="@drawable/audioim_bg_net_excellent"
android:layout_width="6dp"
android:layout_height="6dp"/>
<TextView
android:id="@+id/tvQuality"
android:layout_width="wrap_content"
android:layout_marginLeft="4dp"
android:layout_height="wrap_content"
android:textColor="#99FFFFFF"
android:textSize="13sp"
tools:text="12ms" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/rl_head" android:id="@+id/rl_head"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="315dp" android:layout_width="315dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
...@@ -34,6 +33,7 @@ ...@@ -34,6 +33,7 @@
android:src="@drawable/audioim_switch_axb" /> android:src="@drawable/audioim_switch_axb" />
<TextView <TextView
android:id="@+id/tvTip"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
......
...@@ -11,9 +11,10 @@ import androidx.databinding.DataBindingUtil ...@@ -11,9 +11,10 @@ import androidx.databinding.DataBindingUtil
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.blankj.utilcode.constant.PermissionConstants import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.PermissionUtils import com.blankj.utilcode.util.PermissionUtils
import com.ydl.audioim.api.IAudioImService
import com.ydl.confide.R import com.ydl.confide.R
import com.ydl.confide.databinding.DialogConfidePermissionBinding import com.ydl.confide.databinding.DialogConfidePermissionBinding
import com.ydl.ydlcommon.view.dialog.CommonDialog import com.ydl.ydlcommon.modular.findRouteService
class ConfidePermissionDialog : DialogFragment() { class ConfidePermissionDialog : DialogFragment() {
...@@ -58,13 +59,14 @@ class ConfidePermissionDialog : DialogFragment() { ...@@ -58,13 +59,14 @@ class ConfidePermissionDialog : DialogFragment() {
private fun reject() { private fun reject() {
dismissAllowingStateLoss() dismissAllowingStateLoss()
CommonDialog(context) // CommonDialog(context)
.setMessage("由于您尚未同意获取麦克风权限,本次通话将使用传统电话方式拨打") // .setMessage("由于您尚未同意获取麦克风权限,本次通话将使用传统电话方式拨打")
.setRightClick("确定") { // .setRightClick("确定") {
callback?.invoke(false) // callback?.invoke(false)
} // }
.setCancelAble(false) // .setCancelAble(false)
.show() // .show()
findRouteService(IAudioImService::class.java).openPermissionRejectDialog(activity) { callback?.invoke(false) }
} }
override fun onStart() { override fun onStart() {
......
...@@ -98,6 +98,11 @@ class ExpertSearchActivity : BaseMvpActivity<IExpertSearchView, ExpertSearchPres ...@@ -98,6 +98,11 @@ class ExpertSearchActivity : BaseMvpActivity<IExpertSearchView, ExpertSearchPres
override fun showPromptPayment(promptPaymentBean: PromptPaymentBean) { override fun showPromptPayment(promptPaymentBean: PromptPaymentBean) {
} }
override fun consultTopBannerBean(consultantTopData: MutableList<ConsultantTopBannerBean>) {
}
override fun showImage( override fun showImage(
url: String?, url: String?,
imgView: ImageView, imgView: ImageView,
......
...@@ -47,6 +47,7 @@ import com.ydl.ydlcommon.view.listener.EndlessRecyclerViewScrollListener ...@@ -47,6 +47,7 @@ import com.ydl.ydlcommon.view.listener.EndlessRecyclerViewScrollListener
import com.yidianling.common.tools.LogUtil import com.yidianling.common.tools.LogUtil
import com.yidianling.common.tools.RxImageTool import com.yidianling.common.tools.RxImageTool
import com.yidianling.common.tools.ToastUtil import com.yidianling.common.tools.ToastUtil
import com.yidianling.consultant.adapter.ConsultantTopAdapter
import com.yidianling.consultant.adapter.ExpertSearchAdapter import com.yidianling.consultant.adapter.ExpertSearchAdapter
import com.yidianling.consultant.bean.ConsultantRecentCity import com.yidianling.consultant.bean.ConsultantRecentCity
import com.yidianling.consultant.constants.ConsultBIConstants import com.yidianling.consultant.constants.ConsultBIConstants
...@@ -98,7 +99,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -98,7 +99,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
private var locationList: ArrayList<SubItem>? = arrayListOf() private var locationList: ArrayList<SubItem>? = arrayListOf()
private var locationHistoryList: ArrayList<RegionItem>? = arrayListOf() private var locationHistoryList: ArrayList<RegionItem>? = arrayListOf()
private var eightcategoryPopup: EightCategoryPopupWindow? = null private var eightcategoryPopup: EightCategoryPopupWindow? = null
private var mConsultantTopAdapter: ConsultantTopAdapter? = null
override fun layoutResId(): Int { override fun layoutResId(): Int {
return R.layout.consultant_activity_expert_search_list return R.layout.consultant_activity_expert_search_list
} }
...@@ -113,17 +114,9 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -113,17 +114,9 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
title_layout.setPadding(DisplayUtils.dp2px(context, 15), 0, 0, 0) title_layout.setPadding(DisplayUtils.dp2px(context, 15), 0, 0, 0)
initViews() initViews()
initData() initData()
//资源页顶部资源位改版。
val ffrom = PlatformDataManager.getRam().getChannelName() rl_hot_fix_for_huawei.visibility = View.GONE
if (!TextUtils.isEmpty(ffrom) && consultant_list.visibility = View.VISIBLE
(ffrom.endsWith(ChannelConfig.Xinliceshiyiyu.name))
) {
//抑郁焦虑测试
rl_hot_fix_for_huawei.visibility = View.GONE
} else {
rl_hot_fix_for_huawei.visibility = View.VISIBLE
}
} }
override fun initDataAndEventLazy() { override fun initDataAndEventLazy() {
...@@ -155,6 +148,16 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -155,6 +148,16 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
} }
} }
override fun consultTopBannerBean(consultantTopData: MutableList<ConsultantTopBannerBean>) {
//咨询页顶部滑动banner。
consultant_list.visibility = View.VISIBLE
val layoutManager = LinearLayoutManager(requireContext())
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
consultant_list.layoutManager = layoutManager
mConsultantTopAdapter = ConsultantTopAdapter(requireContext(), consultantTopData)
consultant_list.adapter = mConsultantTopAdapter
}
override fun showImage( override fun showImage(
url: String?, url: String?,
imgView: ImageView, imgView: ImageView,
...@@ -386,6 +389,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -386,6 +389,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
getPresenter().fetchListHead() getPresenter().fetchListHead()
getPresenter().fetchFunctionWord(2) getPresenter().fetchFunctionWord(2)
getPresenter().fetchConsultTopBanner()
recommendListView.requestData("") recommendListView.requestData("")
v_loading.visibility = View.VISIBLE v_loading.visibility = View.VISIBLE
...@@ -1015,14 +1019,14 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -1015,14 +1019,14 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
tempFilter, this tempFilter, this
) )
} }
pricePopup!!.setOnDismissListener { pricePopup?.setOnDismissListener {
if (tempFilter.priceRanges != null) { if (tempFilter.priceRanges != null) {
updateFilterTextViewStatus(tvSort, FILTER_STATUS_FILTERED) updateFilterTextViewStatus(tvSort, FILTER_STATUS_FILTERED)
} else { } else {
updateFilterTextViewStatus(tvSort, FILTER_STATUS_NORMAL) updateFilterTextViewStatus(tvSort, FILTER_STATUS_NORMAL)
} }
} }
pricePopup!!.showAsDropDown(viewSep2) pricePopup?.showAsDropDown(viewSep2)
} }
//显示筛选弹窗 //显示筛选弹窗
...@@ -1600,6 +1604,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres ...@@ -1600,6 +1604,7 @@ class ExpertSearchFragment : BaseMvpFragment<IExpertSearchView, ExpertSearchPres
onScrollListener.resetState() onScrollListener.resetState()
getPresenter().mExtras = null getPresenter().mExtras = null
getPresenter().fetchListData(allFilter, getPresenter().mExtras, null) getPresenter().fetchListData(allFilter, getPresenter().mExtras, null)
getPresenter().fetchConsultTopBanner()
rvExperts.scrollToPosition(0) rvExperts.scrollToPosition(0)
} }
......
...@@ -63,6 +63,21 @@ class ExpertSearchPresenter : SimplePresenter<IExpertSearchView>() { ...@@ -63,6 +63,21 @@ class ExpertSearchPresenter : SimplePresenter<IExpertSearchView>() {
} }
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
fun fetchConsultTopBanner() {
SearchApi.getSearchApi()
.searchConsultTopicBanner()
.compose(RxLifecycleUtils.bindToLifecycle(mView))
.compose(RxUtils.resultJavaData())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ resp ->
mView.consultTopBannerBean(resp)
}, { t ->
HttpErrorUtils.handleError(BaseApp.getApp(), t)
})
}
@SuppressLint("CheckResult")
fun fetchListData(allFilter: AllFilter, extras: Extras?, mapFilter: Map<String, Any>?) { fun fetchListData(allFilter: AllFilter, extras: Extras?, mapFilter: Map<String, Any>?) {
//是否亲子教育字段 //是否亲子教育字段
// ConsultAssistantDialogUtils.REALATION_EDUCATION = allFilter.categories.size == 1 && allFilter.categories[0].cateId == "23" // ConsultAssistantDialogUtils.REALATION_EDUCATION = allFilter.categories.size == 1 && allFilter.categories[0].cateId == "23"
......
...@@ -28,13 +28,15 @@ interface IExpertSearchView : IView { ...@@ -28,13 +28,15 @@ interface IExpertSearchView : IView {
/** /**
* 加载图片 * 加载图片
*/ */
fun showImage(url : String?, imgView : ImageView) fun showImage(url: String?, imgView: ImageView)
/** /**
* 加载图片 * 加载图片
*/ */
fun showImage(url : String?, imgView : ImageView, ops : SimpleImageOpConfiger) fun showImage(url: String?, imgView: ImageView, ops: SimpleImageOpConfiger)
fun showPromptPayment(promptPaymentBean: PromptPaymentBean) fun showPromptPayment(promptPaymentBean: PromptPaymentBean)
fun consultTopBannerBean(consultantTopData: MutableList<ConsultantTopBannerBean>)
} }
\ No newline at end of file
package com.yidianling.consultant.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import com.ydl.webview.H5Params
import com.ydl.webview.NewH5Activity
import com.ydl.ydl_image.module.GlideApp
import com.yidianling.consultant.R
import com.yidianling.consultant.model.bean.ConsultantTopBannerBean
import kotlinx.android.synthetic.main.consultant_top_banner_item.view.*
/**
* Created by zqk on 17-9-20.
*/
class ConsultantTopAdapter(
private val context: Context,
private val consultList: MutableList<ConsultantTopBannerBean>
) : RecyclerView.Adapter<ConsultantTopAdapter.ViewHolder>() {
override fun getItemCount(): Int = consultList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView =
LayoutInflater.from(context).inflate(R.layout.consultant_top_banner_item, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val layoutParams = ViewGroup.LayoutParams(
(context.getResources().getDisplayMetrics().widthPixels / 4.5).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
holder.itemView.layoutParams = layoutParams
if (consultList[position].specialTopicSubhead?.isNotEmpty() == true) {
holder.consult_tag.visibility = View.VISIBLE
holder.consult_tag.text = consultList[position].specialTopicSubhead
if (consultList[position].specialTopicSubhead?.toCharArray()?.size!! <= 2) {
val params = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
)
params.setMargins(46, 0, 0, 0)
holder.consult_tag.layoutParams = params
}
} else {
holder.consult_tag.visibility = View.GONE
}
GlideApp.with(context)
.load(consultList[position].topicImage)
.into(holder.consult_img)
holder.consult_title.text = consultList[position].specialTopicTitle
holder.itemView.setOnClickListener {
NewH5Activity.start(
context,
H5Params(consultList[position].specialTopicUrl.toString(), "")
)
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val consult_tag = itemView.consult_tag
val consult_img = itemView.consult_img
val consult_title = itemView.consult_title
}
}
\ No newline at end of file
...@@ -50,6 +50,11 @@ interface SearchApi { ...@@ -50,6 +50,11 @@ interface SearchApi {
@Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA) @Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA)
fun searchFunctionWord(@Body map: Map<String, @JvmSuppressWildcards Any>): Observable<BaseAPIResponse<MutableList<FunctionWordBean>>> fun searchFunctionWord(@Body map: Map<String, @JvmSuppressWildcards Any>): Observable<BaseAPIResponse<MutableList<FunctionWordBean>>>
//咨询页顶部滑动banner
@POST("doctor/v1/querySpecialTopic")
@Headers(YDL_DOMAIN + YDL_DOMAIN_JAVA)
fun searchConsultTopicBanner(): Observable<BaseAPIResponse<MutableList<ConsultantTopBannerBean>>>
//查询功能词、底纹词 //查询功能词、底纹词
//(1热门词,2底纹词,3跳转词) //(1热门词,2底纹词,3跳转词)
@POST("function/word/queryList") @POST("function/word/queryList")
......
package com.yidianling.consultant.model.bean
data class ConsultantTopBannerBean(
val specialTopicSubhead: String? = "",//右上角标签 有数据则展示
val specialTopicUrl: String? = "",
val topicImage: String? = "",
val specialTopicTitle: String? = ""
)
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingDefaultResource">
<corners
android:bottomRightRadius="@dimen/platform_dp_3"
android:topLeftRadius="@dimen/platform_dp_3"
android:topRightRadius="@dimen/platform_dp_3" />
<solid android:color="#ff7171" />
</shape>
\ No newline at end of file
...@@ -55,6 +55,14 @@ ...@@ -55,6 +55,14 @@
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</RelativeLayout> </RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/consultant_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="horizontal"
android:paddingBottom="@dimen/platform_dp_17"
android:visibility="visible" />
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/platform_dp_25"
android:background="@color/white">
<FrameLayout
android:id="@+id/fl_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/consult_img"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginTop="@dimen/platform_dp_10" />
<TextView
android:id="@+id/consult_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/platform_dp_12"
android:layout_marginBottom="@dimen/platform_dp_40"
android:background="@drawable/consultant_bg_top_tag"
android:gravity="center"
android:includeFontPadding="false"
android:maxLength="8"
android:minWidth="@dimen/platform_dp_25"
android:paddingStart="@dimen/platform_dp_4"
android:paddingTop="@dimen/platform_dp_2"
android:paddingEnd="@dimen/platform_dp_4"
android:paddingBottom="@dimen/platform_dp_2"
android:textColor="@color/white"
android:textSize="9sp"
tools:text="dd" />
</FrameLayout>
<TextView
android:id="@+id/consult_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/platform_dp_8"
android:includeFontPadding="false"
android:textColor="@color/platform_color_666666"
android:textSize="@dimen/platform_sp_12"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fl_img"
tools:text="新人专区">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -60,7 +60,7 @@ dependencies { ...@@ -60,7 +60,7 @@ dependencies {
api rootProject.ext.dependencies["ydl-user-router"] api rootProject.ext.dependencies["ydl-user-router"]
implementation rootProject.ext.dependencies["BaseRecyclerViewAdapterHelper"] implementation rootProject.ext.dependencies["BaseRecyclerViewAdapterHelper"]
implementation('com.netease.yunxin.kit:call-ui:1.5.9-SNAPSHOT') { implementation('com.netease.yunxin.kit:call-ui:1.6.3') {
exclude group: "com.netease.nimlib", module: "avsignalling" exclude group: "com.netease.nimlib", module: "avsignalling"
exclude group: "com.netease.nimlib", module: "basesdk" exclude group: "com.netease.nimlib", module: "basesdk"
} }
...@@ -69,6 +69,7 @@ dependencies { ...@@ -69,6 +69,7 @@ dependencies {
api project(':ydl-webview') api project(':ydl-webview')
api project(':ydl-platform') api project(':ydl-platform')
implementation project(":api:im") implementation project(":api:im")
implementation project(":api:audioim")
implementation project(":api:user") implementation project(":api:user")
implementation project(":api:dynamic") implementation project(":api:dynamic")
implementation project(":api:tests") implementation project(":api:tests")
......
package com.yidianling.im.helper; package com.yidianling.im.helper;
import com.yidianling.uikit.api.NimUIKit;
import com.netease.nimlib.sdk.NIMClient; import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.auth.AuthService; import com.netease.nimlib.sdk.auth.AuthService;
import com.yidianling.im.preference.IMCache; import com.yidianling.im.preference.IMCache;
import com.yidianling.nimbase.common.http.NimHttpClient; import com.yidianling.nimbase.common.http.NimHttpClient;
import com.yidianling.uikit.api.NimUIKit;
/** /**
* 注销帮助类 * 注销帮助类
......
...@@ -2,18 +2,21 @@ package com.yidianling.im.helper; ...@@ -2,18 +2,21 @@ package com.yidianling.im.helper;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import com.netease.lava.nertc.sdk.NERtcConstants;
import com.netease.lava.nertc.sdk.NERtcEx; import com.netease.lava.nertc.sdk.NERtcEx;
import com.netease.lava.nertc.sdk.NERtcOption; import com.netease.lava.nertc.sdk.NERtcOption;
import com.netease.lava.nertc.sdk.NERtcParameters;
import com.netease.lava.nertc.sdk.video.NERtcEncodeConfig; import com.netease.lava.nertc.sdk.video.NERtcEncodeConfig;
import com.netease.lava.nertc.sdk.video.NERtcVideoConfig; import com.netease.lava.nertc.sdk.video.NERtcVideoConfig;
import com.netease.nimlib.sdk.RequestCallback; import com.netease.nimlib.sdk.RequestCallback;
import com.netease.yunxin.nertc.nertcvideocall.bean.InvitedInfo; import com.netease.yunxin.nertc.nertcvideocall.bean.InvitedInfo;
import com.netease.yunxin.nertc.nertcvideocall.model.NERtcCallExtension;
import com.netease.yunxin.nertc.ui.CallKitNotificationConfig; import com.netease.yunxin.nertc.ui.CallKitNotificationConfig;
import com.netease.yunxin.nertc.ui.CallKitUI; import com.netease.yunxin.nertc.ui.CallKitUI;
import com.netease.yunxin.nertc.ui.CallKitUIOptions; import com.netease.yunxin.nertc.ui.CallKitUIOptions;
import com.netease.yunxin.nertc.ui.extension.SelfConfigExtension;
import com.netease.yunxin.nertc.ui.service.DefaultIncomingCallEx; import com.netease.yunxin.nertc.ui.service.DefaultIncomingCallEx;
import com.ydl.ydlcommon.modular.ModularServiceManagerKt; import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.ydl.ydlcommon.utils.log.XLog; import com.ydl.ydlcommon.utils.log.XLog;
...@@ -42,8 +45,14 @@ public class NimUICallInit { ...@@ -42,8 +45,14 @@ public class NimUICallInit {
UserResponseBean.UserInfo userInfo = ModularServiceManagerKt.findRouteService(IUserService.class).getUserInfo(); UserResponseBean.UserInfo userInfo = ModularServiceManagerKt.findRouteService(IUserService.class).getUserInfo();
if (userInfo == null) return; if (userInfo == null) return;
String userId = userInfo.getUid(); String userId = userInfo.getUid();
android.util.Log.e("qwert", userId); //防止网络波动的情况下多次触发LOGINED状态,导致呼叫组件重新初始化
if (TextUtils.equals(userId, CallKitUI.INSTANCE.getCurrentUserAccId())) return;
android.util.Log.e("qwert", userId + (Looper.myLooper() == Looper.getMainLooper()));
String appKey = NimApplication.getInstance().getAppKey(); String appKey = NimApplication.getInstance().getAppKey();
NERtcParameters neRtcParameters = new NERtcParameters();
neRtcParameters.set(NERtcParameters.KEY_SERVER_RECORD_AUDIO, true);
neRtcParameters.set(NERtcParameters.KEY_SERVER_RECORD_VIDEO, true);
NERtcEx.getInstance().setParameters(neRtcParameters);
CallKitUIOptions options = new CallKitUIOptions.Builder() CallKitUIOptions options = new CallKitUIOptions.Builder()
// 必要:音视频通话 sdk appKey,用于通话中使用 // 必要:音视频通话 sdk appKey,用于通话中使用
.rtcAppKey(appKey) .rtcAppKey(appKey)
...@@ -53,15 +62,20 @@ public class NimUICallInit { ...@@ -53,15 +62,20 @@ public class NimUICallInit {
.currentUserRtcUId(Long.parseLong(userId)) .currentUserRtcUId(Long.parseLong(userId))
// 通话接听成功的超时时间单位 毫秒,默认30s // 通话接听成功的超时时间单位 毫秒,默认30s
.timeOutMillisecond(30 * 1000L) .timeOutMillisecond(30 * 1000L)
.rtcCallExtension(new SelfConfigExtension(){ .rtcCallExtension(new NERtcCallExtension(){
@Override @Override
public void configVideoConfig() { protected int joinChannel(String token, String channelName, long rtcUid) {
NERtcVideoConfig videoConfig = new NERtcVideoConfig(); NERtcVideoConfig videoConfig = new NERtcVideoConfig();
videoConfig.frameRate = NERtcEncodeConfig.NERtcVideoFrameRate.FRAME_RATE_FPS_15; videoConfig.frameRate = NERtcEncodeConfig.NERtcVideoFrameRate.FRAME_RATE_FPS_15;
videoConfig.bitrate = 600;
videoConfig.width = 640; videoConfig.width = 640;
videoConfig.height = 480; videoConfig.height = 480;
videoConfig.degradationPrefer = NERtcVideoConfig.NERtcDegradationPreference.DEGRADATION_MAINTAIN_FRAMERATE;
NERtcEx.getInstance().setLocalVideoConfig(videoConfig); NERtcEx.getInstance().setLocalVideoConfig(videoConfig);
// 音频设置 standard + speech
NERtcEx.getInstance().setAudioProfile(NERtcConstants.AudioProfile.STANDARD, NERtcConstants.AudioScenario.SPEECH);
// 设置 channel profile
NERtcEx.getInstance().setChannelProfile(NERtcConstants.RTCChannelProfile.COMMUNICATION);
return NERtcEx.getInstance().joinChannel(token, channelName, rtcUid);
} }
}) })
// 此处为 收到来电时展示的 notification 相关配置,如图标,提示语等。 // 此处为 收到来电时展示的 notification 相关配置,如图标,提示语等。
...@@ -80,9 +94,12 @@ public class NimUICallInit { ...@@ -80,9 +94,12 @@ public class NimUICallInit {
// .p2pAudioActivity(TestActivity.class) // .p2pAudioActivity(TestActivity.class)
// .p2pVideoActivity(TestActivity.class) // .p2pVideoActivity(TestActivity.class)
// 请求 rtc token 服务,若非安全模式则不需设置 // 请求 rtc token 服务,若非安全模式则不需设置
.rtcTokenService((uid, callback) -> requestRtcToken(uid, callback)) // 自己实现的 token 请求方法 .rtcTokenService((uid, channel, callback) -> requestRtcToken(uid, channel, callback)) // 自己实现的 token 请求方法
// 设置初始化 rtc sdk 相关配置,按照所需进行配置 // 设置初始化 rtc sdk 相关配置,按照所需进行配置
.rtcSdkOption(new NERtcOption()) .rtcSdkOption(new NERtcOption())
.enableMsgFilter(false)
.enableCustomParser(false)
.audio2VideoConfirm(true)
// 呼叫组件初始化 rtc 范围,true-全局初始化,false-每次通话进行初始化以及销毁 // 呼叫组件初始化 rtc 范围,true-全局初始化,false-每次通话进行初始化以及销毁
// 全局初始化有助于更快进入首帧页面,当结合其他组件使用时存在rtc初始化冲突可设置false // 全局初始化有助于更快进入首帧页面,当结合其他组件使用时存在rtc初始化冲突可设置false
.rtcInitScope(true) .rtcInitScope(true)
...@@ -92,10 +109,10 @@ public class NimUICallInit { ...@@ -92,10 +109,10 @@ public class NimUICallInit {
} }
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private static void requestRtcToken(long uid, RequestCallback<String> callback) { private static void requestRtcToken(long uid, String channel, RequestCallback<String> callback) {
Map<String, String> body = new HashMap<>(); Map<String, String> body = new HashMap<>();
if (curChannelName != null) { if (channel != null) {
body.put("channelName", curChannelName); body.put("channelName", channel);
} }
body.put("uid", String.valueOf(uid)); body.put("uid", String.valueOf(uid));
ImHttpImpl.Companion.getInstance().getImApi().nim2Token(body) ImHttpImpl.Companion.getInstance().getImApi().nim2Token(body)
......
...@@ -16,6 +16,8 @@ import com.netease.nimlib.sdk.msg.MsgService ...@@ -16,6 +16,8 @@ import com.netease.nimlib.sdk.msg.MsgService
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum
import com.netease.nimlib.sdk.msg.model.CustomNotification import com.netease.nimlib.sdk.msg.model.CustomNotification
import com.netease.nimlib.sdk.msg.model.CustomNotificationConfig import com.netease.nimlib.sdk.msg.model.CustomNotificationConfig
import com.tencent.mmkv.MMKV
import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.router.YdlCommonOut import com.ydl.ydlcommon.router.YdlCommonOut
import com.ydl.ydlcommon.utils.StringUtils import com.ydl.ydlcommon.utils.StringUtils
import com.ydl.ydlcommon.utils.remind.HttpErrorUtils import com.ydl.ydlcommon.utils.remind.HttpErrorUtils
...@@ -440,4 +442,15 @@ class IMServiceImpl : IImService { ...@@ -440,4 +442,15 @@ class IMServiceImpl : IImService {
P2PCustomActionHandlerImpl(doctorId + "") P2PCustomActionHandlerImpl(doctorId + "")
) )
} }
@SuppressLint("CheckResult")
override fun getSystemConfig(key: String) {
ImRetrofitApi.getImRetrofitApi()
.getSystemConfigByKeyword(key)
.compose(RxUtils.resultJavaData())
.subscribeOn(Schedulers.io())
.subscribe {
MMKV.defaultMMKV().putString(key, it.value1)
}
}
} }
\ No newline at end of file
...@@ -11,11 +11,13 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{ ...@@ -11,11 +11,13 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
private final String KEY_CALLEE="callee"; private final String KEY_CALLEE="callee";
private final String KEY_DURATION="duration"; private final String KEY_DURATION="duration";
private final String KEY_IS_CONSULT_ORDER = "isConsultOrder"; //是否是咨询单,true是 private final String KEY_IS_CONSULT_ORDER = "isConsultOrder"; //是否是咨询单,true是
private final String KEY_PULL_CALL = "pullCall"; //是否是咨询单,true是
private String status; private String status;
private String caller; private String caller;
private String callee; private String callee;
private String duration; private String duration;
private String isConsultOrder; private String isConsultOrder;
private Integer pullCall;//标识是可以拉起通话的 0或空 则不拉起
public CustomAttachConsultCallStatus() { public CustomAttachConsultCallStatus() {
super(CustomAttachmentType.TYPE_CUSTOMER_CONSULT_CALL_STATUS); super(CustomAttachmentType.TYPE_CUSTOMER_CONSULT_CALL_STATUS);
...@@ -28,6 +30,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{ ...@@ -28,6 +30,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
this.callee=data.getString(KEY_CALLEE); this.callee=data.getString(KEY_CALLEE);
this.duration=data.getString(KEY_DURATION); this.duration=data.getString(KEY_DURATION);
this.isConsultOrder=data.getString(KEY_IS_CONSULT_ORDER); this.isConsultOrder=data.getString(KEY_IS_CONSULT_ORDER);
this.pullCall = data.getInteger(KEY_PULL_CALL);
} }
@Override @Override
...@@ -38,6 +41,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{ ...@@ -38,6 +41,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
data.put(KEY_CALLEE, callee); data.put(KEY_CALLEE, callee);
data.put(KEY_DURATION, duration); data.put(KEY_DURATION, duration);
data.put(KEY_IS_CONSULT_ORDER, isConsultOrder); data.put(KEY_IS_CONSULT_ORDER, isConsultOrder);
data.put(KEY_PULL_CALL, pullCall);
return data; return data;
} }
...@@ -58,4 +62,8 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{ ...@@ -58,4 +62,8 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
} }
public String getIsConsultOrder() {return isConsultOrder;} public String getIsConsultOrder() {return isConsultOrder;}
public Integer getPullCall() {
return pullCall;
}
} }
\ No newline at end of file
...@@ -4,6 +4,8 @@ import android.view.View; ...@@ -4,6 +4,8 @@ import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.ydl.audioim.api.IAudioImService;
import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.yidianling.im.R; import com.yidianling.im.R;
import com.yidianling.im.session.extension.CustomAttachConsultCallStatus; import com.yidianling.im.session.extension.CustomAttachConsultCallStatus;
import com.yidianling.nimbase.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter; import com.yidianling.nimbase.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter;
...@@ -17,6 +19,8 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase { ...@@ -17,6 +19,8 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
private ImageView typeImageLeft; private ImageView typeImageLeft;
private ImageView typeImageRight; private ImageView typeImageRight;
private TextView statusLabel; private TextView statusLabel;
private View lineView;
private TextView btnView;
public MsgViewHolderConsultCallStatus(BaseMultiItemFetchLoadAdapter adapter) { public MsgViewHolderConsultCallStatus(BaseMultiItemFetchLoadAdapter adapter) {
super(adapter); super(adapter);
...@@ -29,9 +33,11 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase { ...@@ -29,9 +33,11 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
@Override @Override
protected void inflateContentView() { protected void inflateContentView() {
typeImageLeft = findViewById(R.id.type_img_left); typeImageLeft = findViewById(R.id.message_item_type_img_left);
typeImageRight = findViewById(R.id.type_img_right); typeImageRight = findViewById(R.id.message_item_type_img_right);
statusLabel = findViewById(R.id.tv_state); statusLabel = findViewById(R.id.message_item_avchat_state);
lineView = findViewById(R.id.message_item_avchat_line);
btnView = findViewById(R.id.message_item_avchat_btn);
} }
@Override @Override
...@@ -57,6 +63,21 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase { ...@@ -57,6 +63,21 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
} else { } else {
statusLabel.setText(customAttachTipMsg.getCaller()); statusLabel.setText(customAttachTipMsg.getCaller());
} }
long messageTime = message.getTime();
boolean isExpired = System.currentTimeMillis() - messageTime > 60 * 1000;
Integer pullCall = customAttachTipMsg.getPullCall();
if (!isExpired && pullCall != null && pullCall == 1 && isReceivedMessage()) {
lineView.setVisibility(View.VISIBLE);
btnView.setVisibility(View.VISIBLE);
btnView.setOnClickListener(v -> {
String sessionId = message.getSessionId();
ModularServiceManagerKt.findRouteService(IAudioImService.class)
.callCheck(v.getContext(), sessionId, 0);
});
} else {
lineView.setVisibility(View.GONE);
btnView.setVisibility(View.GONE);
}
} }
@Override @Override
...@@ -78,5 +99,4 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase { ...@@ -78,5 +99,4 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
return R.drawable.im_message_send_content_bg; return R.drawable.im_message_send_content_bg;
} }
} }
} }
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="40dp" />
<gradient
android:endColor="#159CEF"
android:startColor="#22BFFF" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_content" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/message_item_avchat_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="6dp"
android:minHeight="40dp" android:minHeight="40dp"
android:orientation="horizontal"> android:orientation="vertical"
android:padding="6dp">
<ImageView <ImageView
android:id="@+id/type_img_left" android:id="@+id/message_item_type_img_left"
android:layout_width="14dp" android:layout_width="30dp"
android:layout_height="14dp" android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="3dp" android:layout_marginLeft="3dp"
android:layout_marginRight="3dp" android:layout_marginRight="3dp"
android:contentDescription="@string/im_empty" android:src="@drawable/im_avchat_type_audio"
android:visibility="gone" android:visibility="gone"
android:src="@drawable/im_avchat_type_audio"/> android:scaleType="center"
app:layout_constraintBottom_toBottomOf="@+id/message_item_avchat_state"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@+id/message_item_avchat_state" />
<TextView <TextView
android:id="@+id/tv_state" android:id="@+id/message_item_avchat_state"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp" android:layout_marginLeft="5dp"
android:layout_marginTop="4dp"
android:layout_marginRight="5dp" android:layout_marginRight="5dp"
android:text="无人接听" android:text="无人接听111111111"
android:textColor="@color/platform_color_242424" android:textColor="@color/color_242424"
android:textSize="14sp"/> android:textSize="15sp"
app:layout_constraintLeft_toRightOf="@+id/message_item_type_img_left"
app:layout_constraintRight_toLeftOf="@+id/message_item_type_img_right"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:visibility="gone" android:id="@+id/message_item_type_img_right"
android:id="@+id/type_img_right" android:layout_width="30dp"
android:layout_width="14dp" android:layout_height="30dp"
android:layout_height="14dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginLeft="3dp" android:layout_marginLeft="3dp"
android:layout_marginRight="3dp" android:layout_marginRight="3dp"
android:contentDescription="@string/im_empty" android:src="@drawable/im_avchat_type_audio"
android:src="@drawable/im_avchat_type_audio"/> android:visibility="gone"
</LinearLayout> android:scaleType="center"
app:layout_constraintBottom_toBottomOf="@+id/message_item_avchat_state"
app:layout_constraintLeft_toRightOf="@+id/message_item_avchat_state"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/message_item_avchat_state" />
<View
android:id="@+id/message_item_avchat_line"
android:layout_width="0dp"
android:layout_height="0.5dp"
android:layout_marginTop="12dp"
android:background="@color/platform_color_EBEBEB"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/message_item_avchat_state" />
<TextView
android:id="@+id/message_item_avchat_btn"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_marginTop="10dp"
android:layout_marginRight="4dp"
android:background="@drawable/bg_btn1"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="立即接听"
android:textColor="@color/white"
android:layout_marginBottom="4dp"
android:textSize="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/message_item_avchat_line" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -59,6 +59,7 @@ dependencies { ...@@ -59,6 +59,7 @@ dependencies {
// implementation project(":ydl-tuicore") // implementation project(":ydl-tuicore")
implementation project(":api:user") implementation project(":api:user")
implementation project(":api:course") implementation project(":api:course")
implementation project(":api:audioim")
implementation project(":api:im") implementation project(":api:im")
implementation project(":api:fm") implementation project(":api:fm")
implementation project(":api:dynamic") implementation project(":api:dynamic")
......
...@@ -2,7 +2,9 @@ package com.yidianling.user ...@@ -2,7 +2,9 @@ package com.yidianling.user
import android.app.Activity import android.app.Activity
import com.tencent.bugly.crashreport.CrashReport import com.tencent.bugly.crashreport.CrashReport
import com.ydl.audioim.api.IAudioImService
import com.ydl.ydlcommon.data.http.RxUtils import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.modular.findRouteService
import com.ydl.ydlcommon.utils.ActivityManager import com.ydl.ydlcommon.utils.ActivityManager
import com.ydl.ydlcommon.utils.BuryPointUtils import com.ydl.ydlcommon.utils.BuryPointUtils
import com.ydl.ydlcommon.utils.log.AliYunLogConfig import com.ydl.ydlcommon.utils.log.AliYunLogConfig
...@@ -88,6 +90,7 @@ object LoginUtils { ...@@ -88,6 +90,7 @@ object LoginUtils {
@JvmStatic @JvmStatic
fun logout() { fun logout() {
EventBus.getDefault().post(UserLogoutEvent()) EventBus.getDefault().post(UserLogoutEvent())
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLogout", "用户登出", null)
UserHttpImpl.getInstance().logout(Logout()) UserHttpImpl.getInstance().logout(Logout())
.compose(RxUtils.resultData()) .compose(RxUtils.resultData())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
......
...@@ -10,14 +10,15 @@ import android.widget.ToggleButton; ...@@ -10,14 +10,15 @@ import android.widget.ToggleButton;
import com.umeng.socialize.UMAuthListener; import com.umeng.socialize.UMAuthListener;
import com.umeng.socialize.UMShareAPI; import com.umeng.socialize.UMShareAPI;
import com.umeng.socialize.bean.SHARE_MEDIA; import com.umeng.socialize.bean.SHARE_MEDIA;
import com.ydl.audioim.api.IAudioImService;
import com.ydl.webview.H5Params; import com.ydl.webview.H5Params;
import com.ydl.webview.NewH5Activity; import com.ydl.webview.NewH5Activity;
import com.ydl.ydlcommon.base.BaseActivity; import com.ydl.ydlcommon.base.BaseActivity;
import com.ydl.ydlcommon.bean.StatusBarOptions; import com.ydl.ydlcommon.bean.StatusBarOptions;
import com.ydl.ydlcommon.data.http.ThrowableConsumer; import com.ydl.ydlcommon.data.http.ThrowableConsumer;
import com.ydl.ydlcommon.modular.ModularServiceManager; import com.ydl.ydlcommon.modular.ModularServiceManager;
import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.ydl.ydlcommon.router.YdlCommonOut; import com.ydl.ydlcommon.router.YdlCommonOut;
import com.ydl.ydlcommon.utils.LogUtil;
import com.ydl.ydlcommon.utils.SharedPreferencesEditor; import com.ydl.ydlcommon.utils.SharedPreferencesEditor;
import com.ydl.ydlcommon.utils.remind.ToastHelper; import com.ydl.ydlcommon.utils.remind.ToastHelper;
import com.ydl.ydlcommon.view.JumpTextView; import com.ydl.ydlcommon.view.JumpTextView;
...@@ -299,8 +300,10 @@ public class AccountSettingActivity extends BaseActivity implements View.OnClick ...@@ -299,8 +300,10 @@ public class AccountSettingActivity extends BaseActivity implements View.OnClick
//清除UserInfo //清除UserInfo
UserHelper.INSTANCE.setUserinfo(null); UserHelper.INSTANCE.setUserinfo(null);
UserIn.INSTANCE.clearImData(); UserIn.INSTANCE.clearImData();
ModularServiceManagerKt.findRouteService(IAudioImService.class).logoutRtm();
ModularServiceManagerKt.findRouteService(IImService.class).logoutAll();
LoginUtils.logout(); LoginUtils.logout();
EventBus.getDefault().post(new RefreshRecentContactListEvent()); EventBus.getDefault().post(new RefreshRecentContactListEvent());
finish(); finish();
} }
......
...@@ -2,7 +2,9 @@ package com.yidianling.user.ui.login.presenter ...@@ -2,7 +2,9 @@ package com.yidianling.user.ui.login.presenter
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.text.TextUtils import android.text.TextUtils
import com.ydl.audioim.api.IAudioImService
import com.ydl.ydlcommon.app.Apm import com.ydl.ydlcommon.app.Apm
import com.ydl.ydlcommon.modular.findRouteService
import com.ydl.ydlcommon.mvp.base.BasePresenter import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.ydl.ydlcommon.router.YdlCommonOut import com.ydl.ydlcommon.router.YdlCommonOut
import com.ydl.ydlcommon.utils.StringUtils import com.ydl.ydlcommon.utils.StringUtils
...@@ -105,6 +107,7 @@ class InputPassWordPresenterImpl : ...@@ -105,6 +107,7 @@ class InputPassWordPresenterImpl :
) )
var param = PhoneLoginPwdParam(StringUtils.md5(inputPassword), phoneCountryCode, phone) var param = PhoneLoginPwdParam(StringUtils.md5(inputPassword), phoneCountryCode, phone)
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLogin", "用户登录", null)
mModel.userLoginByPassword(param) mModel.userLoginByPassword(param)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
...@@ -117,6 +120,7 @@ class InputPassWordPresenterImpl : ...@@ -117,6 +120,7 @@ class InputPassWordPresenterImpl :
ToastUtil.toastShort(it.msg) ToastUtil.toastShort(it.msg)
Apm.reportEvent("login_android", "password_error", "密码登录失败,$phoneCountryCode-$phone") Apm.reportEvent("login_android", "password_error", "密码登录失败,$phoneCountryCode-$phone")
mView.startAnim() mView.startAnim()
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLoginFail", "用户登录失败", null)
} else { } else {
if (it.data.userInfo?.user_type == 2) { if (it.data.userInfo?.user_type == 2) {
mView.showNormalDialog() mView.showNormalDialog()
...@@ -135,6 +139,7 @@ class InputPassWordPresenterImpl : ...@@ -135,6 +139,7 @@ class InputPassWordPresenterImpl :
} }
mView.closeActivity() mView.closeActivity()
} }
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLoginSuccess", "用户登录成功", null)
AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.LOGIN, "手机号密码 登录成功") AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.LOGIN, "手机号密码 登录成功")
} }
......
package com.ydl.ydlcommon.utils.log package com.ydl.ydlcommon.utils.log
import com.apm.insight.log.VLog import com.apm.insight.log.VLog
import com.ydl.ydlcommon.BuildConfig
object XLog { object XLog {
fun d(tag: String, msg: String) { fun d(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.d(tag, msg)
}
VLog.i(tag, msg) VLog.i(tag, msg)
} }
fun i(tag: String, msg: String) { fun i(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.i(tag, msg)
}
VLog.i(tag, msg) VLog.i(tag, msg)
} }
fun w(tag: String, msg: String) { fun w(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.w(tag, msg)
}
VLog.i(tag, msg) VLog.i(tag, msg)
} }
fun e(tag: String, msg: String) { fun e(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.e(tag, msg)
}
VLog.e(tag, msg) VLog.e(tag, msg)
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment