Commit 6eb0de65 by 刘鹏

Merge branch 'feat/qj/net_quality' into 'd/4.4.10'

网络质量项目

See merge request app_android_lib/YDL-Component!338
parents 353d4088 dc374e9e
......@@ -33,10 +33,20 @@ interface IAudioImService : IProvider{
/**
* 拨打语音电话转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 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 {
fun dismissConsultServiceDialog();
/**群聊*/
fun startTeamSession(activity: Activity, tid: String, doctorId: String)
fun getSystemConfig(key:String)
}
\ No newline at end of file
......@@ -37,7 +37,7 @@ android {
multiDexEnabled true
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 {
packagingOptions {
exclude 'META-INF/proguard/coroutines.pro'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
}
dataBinding {
enabled true
......@@ -177,6 +179,9 @@ android {
dependencies {
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 "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation(rootProject.ext.dependencies["appcompat-v7"])
......@@ -213,6 +218,7 @@ dependencies {
implementation project(':m-muse')
implementation project(':m-im')
implementation project(":api:im")
implementation project(":api:audioim")
implementation project(':m-dynamic')
implementation project(':m-course')
implementation project(":api:course")
......
......@@ -27,7 +27,7 @@ import com.ydl.component.mvp.DemoContract
import com.ydl.component.mvp.DemoPresenter
import com.ydl.component.route.PlatformTempCommonRouteImpl
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.modular.ModularServiceManager
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
......@@ -179,7 +179,8 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
YDLavManager.instances.login(
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()
......
......@@ -4,9 +4,10 @@ import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import androidx.multidex.MultiDex;
import android.webkit.WebView;
import androidx.multidex.MultiDex;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.meituan.android.walle.WalleChannelReader;
import com.tencent.qcloud.tuicore.calling.trtccalling.model.TRTCCalling;
......@@ -14,6 +15,7 @@ import com.umeng.analytics.MobclickAgent;
import com.umeng.commonsdk.UMConfigure;
import com.umeng.socialize.PlatformConfig;
import com.umeng.socialize.UMShareAPI;
import com.ydl.audioim.api.IAudioImService;
import com.ydl.component.route.PlatformTempCommonRouteImpl;
import com.ydl.confide.BuildConfig;
import com.ydl.media.audio.AudioPlayer;
......@@ -21,6 +23,7 @@ import com.ydl.media.audio.manager.MediaSessionManager;
import com.ydl.media.audio.manager.NotifyManager;
import com.ydl.ydl_image.manager.YDLImageCacheManager;
import com.ydl.ydlcommon.base.BaseApp;
import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.ydl.ydlcommon.router.YdlCommonRouterManager;
import com.ydl.ydlcommon.utils.AppProgressUtils;
import com.ydl.ydlcommon.utils.Utils;
......@@ -29,7 +32,6 @@ import com.yidianling.common.tools.ToastUtil;
import com.yidianling.consultant.preview.TestImageLoader;
import com.yidianling.consultant.preview.ZoomMediaLoader;
import com.yidianling.course.lifeCallback.CoursePlayLifecycle;
import com.yidianling.course.widget.AudioPlayView;
/**
......@@ -71,12 +73,14 @@ public class ComponentTestApp extends BaseApp {
registerActivityLifecycleCallbacks(new CoursePlayLifecycle());
ModularServiceManagerKt.findRouteService(IAudioImService.class).initAgoraRtc(this);
Fresco.initialize(this);
TRTCCalling.sharedInstance(this).setMdtCallBack(new TRTCCalling.MdtCallBack() {
@Override
public void onReceiveNewInvitation(String roomId) {
ToastUtil.toastShort("电话邀请"+roomId);
ToastUtil.toastShort("电话邀请" + roomId);
}
@Override
......
......@@ -65,7 +65,7 @@ dependencies {
kapt "com.alibaba:arouter-compiler:$arouter_compiler"
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'
api project(':ydl-platform')
......
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.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))
}
}
}
}
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.mp3"
}
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.mp3")
filesName.forEach {
val file = File(soundFile, it)
if (!file.exists()) {
file.createNewFile()
val fileOutputStream = FileOutputStream(file)
context.assets.open(it).copyTo(fileOutputStream)
}
}
}
.observeOn(Schedulers.io())
.subscribe()
}
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ import com.apm.insight.log.VLog
import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraInvitationBean
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.IMEvent
import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.router.AudioImIn
import com.ydl.audioim.utils.AudioLogUtils
......@@ -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.response.CallLocalResponse
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.modular.ModularServiceManager
import com.ydl.ydlcommon.utils.ActivityManager
......@@ -63,6 +65,8 @@ class YDLavManager {
YDLavManager()
}
const val AUDIO_NO_AUTH_ERROR_CODE = "97"//音频权限未通过错误码
var currentChannelId: String? = null
}
......@@ -99,6 +103,7 @@ class YDLavManager {
AliYunLogConfig.AGORA,
"${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}"
)
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerReceived))
val dimension = hashMapOf("call" to "call_received_by_peer")
onConfideEvent(dimension, response?.ChannelId)
......@@ -118,6 +123,8 @@ class YDLavManager {
AliYunLogConfig.AGORA,
"${response?.calleeId}已接收呼叫邀请"
)
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerAccepted))
currentChannelId = response?.ChannelId
//加入声网频道时机修改:主叫收到被叫接受邀请的回调后再加入声网频道
val act = ActivityManager.getInstance().getTopTaskActivity()
if (act is AudioHomeActivity) {
......@@ -135,6 +142,7 @@ class YDLavManager {
AliYunLogConfig.AGORA,
"${response?.calleeId}已拒绝呼叫邀请"
)
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.onPeerRejected))
val dimension = hashMapOf("call" to "call_refused")
onConfideEvent(dimension, response?.ChannelId)
val act = ActivityManager.getInstance().getTopTaskActivity()
......@@ -161,13 +169,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}")
AliYunRichLogsHelper.getInstance().sendRichLog(
AliYunLogConfig.AGORA,
"呼叫${response?.calleeId}用户失败:${response?.response},${errorCode}"
)
AudioApiRequestUtil.reportCallEvent(
response?.ChannelId,
IMEvent(IMEvent.Event.callFail),
errorCode = errorCode,
errorReason = errorMsg
)
val dimension = hashMapOf(
"call" to "call_fail",
"call_fail" to "code${errorCode}"
......@@ -275,44 +289,20 @@ class YDLavManager {
"呼叫邀请被取消:错误原因(${errorCode})"
)
}
callEndStatusUpdate(response?.ChannelId!!, 2, "超时未接听导致的取消呼叫")
callEndStatusUpdate(response?.ChannelId!!, 8, "超时未接听导致的取消呼叫")
//关闭页面
closePage()
}
override fun onOtherMsg(error: String?) {
LogUtil.e("[agora]其它消息:${error}")
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}"
)
}
override fun onCallSuccess(response: CallLocalResponse?) {
AudioApiRequestUtil.reportCallEvent(response?.ChannelId, IMEvent(IMEvent.Event.callSuccess))
}
})
}
fun rtcCall(listenerUid: String?, channelId: String?, sendDoctocrMsg: String?) {
YDLRTMClient.instances.call(listenerUid, channelId, sendDoctocrMsg)
AudioApiRequestUtil.reportCallEvent(channelId, IMEvent(IMEvent.Event.startCall))
sendCustomNotification(listenerUid!!, sendDoctocrMsg!!, "1")
}
......@@ -346,6 +336,7 @@ class YDLavManager {
})
callEndStatusUpdate(channelId, 1, "主叫取消呼叫")
AudioApiRequestUtil.reportCallEvent(channelId, IMEvent(IMEvent.Event.callCancel))
sendCustomNotification(listenerUid, data, "3")
}
......@@ -412,11 +403,13 @@ class YDLavManager {
.observeOn(AndroidSchedulers.mainThread()).subscribe({
if ("200" == it.code) {
LogUtil.e("[agora]登录av的login-uid:$userId")
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.rtmLogin))
YDLRTMClient.instances.login(
LoginParam(userId, it.data.token),
object : LoginCallback {
override fun onSuccess() {
//登陆成功,发起呼叫
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.loginSuccess))
LogUtil.e("[agora]实时消息登录成功")
AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "声网rtm登录成功,uid:$userId")
......@@ -433,6 +426,8 @@ class YDLavManager {
override fun onFailure(msg: String?) {
if (msg != "LOGIN_ERR_ALREADY_LOGGED_IN") {
Apm.reportEvent("rtm_android", "connectionstate_error", msg ?: "")
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.loginFail), errorReason = msg)
}
LogUtil.e("[agora]实时消息登录失败:$msg")
writeAgoraLog(
......@@ -556,6 +551,7 @@ class YDLavManager {
*/
private fun logout(isReLogin: Boolean) {
EventBus.getDefault().unregister(this)
AudioApiRequestUtil.reportCallEvent(YDLVoiceManager.currentChannel, IMEvent(IMEvent.Event.rtmLogout))
YDLRTMClient.instances.logout(object : LoginCallback {
override fun onSuccess() {
//退出登陆成功
......@@ -612,17 +608,11 @@ class YDLavManager {
@SuppressLint("CheckResult")
fun callEndStatusUpdate(channelId: String, endStatus: Int, msg: String) {
AudioApiRequestUtil.callEndStatusUpdate(channelId, endStatus, msg)
fun callEndStatusUpdate(channelId: String?, endStatus: Int, msg: String) {
if (channelId == null) return
AudioApiRequestUtil.callEventSave(channelId, "7", endStatus.toString(), null, msg)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
}, {
LogUtil.d("callEndStatusUpdate error: ${it.message}")
AliYunRichLogsHelper.getInstance()
.sendRichLog(AliYunLogConfig.AGORA, "callEndStatusUpdate error: ${it.message}")
})
.subscribe()
}
/**
......@@ -660,6 +650,12 @@ class YDLavManager {
true
)
LogUtil.i("[agora]onConnectionStateChanged:state:${state} -->reason:$reason")
AudioApiRequestUtil.reportCallEvent(
YDLVoiceManager.currentChannel,
IMEvent(IMEvent.Event.onlineStatusChange),
errorCode = state,
errorReason = reason.toString()
)
AliYunRichLogsHelper.getInstance()
.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
* 星链-倾诉日志请求参数
* */
data class CallEventRequestBody(
var session :String?,
var line :String,
var status :String,
var response :String,
var eventTime :String
var session: String?,
var line: String,
var status: String,
var response: String? = null,
var eventTime: String,
var remark: String? = null,
var eventType: String? = null
)
package com.ydl.audioim.http
import androidx.annotation.IntDef
import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.CallEventRequestBody
......@@ -16,8 +17,10 @@ import com.ydl.ydlcommon.utils.NetworkParamsUtils
import com.ydl.ydlcommon.utils.TimeUtil
import com.ydl.ydlnet.YDLHttpUtils
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.RequestBody
import java.util.*
/**
* @author jiucheng
......@@ -106,29 +109,151 @@ class AudioApiRequestUtil {
.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(
session: String?,
line: String,
status: String,
response: String
response: String?,
remark: String? = null,
eventType: String? = null
): Observable<BaseAPIResponse<Any>> {
var eventTime = TimeUtil.getNowDatetime()
var param = CallEventRequestBody(session, line, status, response, eventTime);
var str = Gson().toJson(param)
val eventTime = TimeUtil.getNowDatetime()
val param = CallEventRequestBody(session, line, status, response, eventTime, remark, eventType);
val str = Gson().toJson(param)
val body = RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
str
) as RequestBody
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
}
}
}
package com.ydl.audioim.http
import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.CallCheckBean
import com.ydl.audioim.bean.ConnectBean
import com.ydl.consultantim.bean.ListenTokenBean
import com.ydl.ydlcommon.base.config.YDL_DOMAIN
......@@ -19,6 +20,10 @@ import retrofit2.http.*
* @date 2018/11/9
*/
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 {
// @POST("auth/listen-order/callback/agora/finish")
// 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")
fun connectException(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
fun connectException(@Body body: RequestBody): Observable<BaseAPIResponse<Any>>
/**
......@@ -69,9 +78,9 @@ interface AudioNetAPi {
fun listenToken(@FieldMap map: Map<String, String>): Observable<BaseResponse<ListenTokenBean>>
@GET("message/call-end/agora")
@Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA)
fun callEndStatusUpdate(@Query("channelId") channelId:String,@Query("endStatus") endStatus:Int,@Query("msg") msg:String): Observable<BaseAPIResponse<Any>>
// @GET("message/call-end/agora")
// @Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA)
// 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")
......
......@@ -5,9 +5,21 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
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.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.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.
......@@ -49,11 +61,66 @@ class AudioImServiceImp : IAudioImService {
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()
}
override fun callEventSave(status: String, res: String, session: String?, line: String) {
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?) {
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
import android.app.Dialog
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.widget.TextView
import com.ydl.audioim.R
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 {
private var activity: Activity? = 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) {
this.activity = activity
......@@ -29,7 +30,10 @@ class AxbConfirmDialog : Dialog {
super.onCreate(savedInstanceState)
if (this.type==2){
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)
}
......
<?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 @@
android:textSize="13sp" />
</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
android:id="@+id/rl_head"
......
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
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
android:id="@+id/rl_head"
......
<?xml version="1.0" encoding="utf-8"?>
<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_height="wrap_content"
android:layout_gravity="center_horizontal"
......@@ -34,6 +33,7 @@
android:src="@drawable/audioim_switch_axb" />
<TextView
android:id="@+id/tvTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
......
......@@ -11,9 +11,10 @@ import androidx.databinding.DataBindingUtil
import androidx.fragment.app.DialogFragment
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.PermissionUtils
import com.ydl.audioim.api.IAudioImService
import com.ydl.confide.R
import com.ydl.confide.databinding.DialogConfidePermissionBinding
import com.ydl.ydlcommon.view.dialog.CommonDialog
import com.ydl.ydlcommon.modular.findRouteService
class ConfidePermissionDialog : DialogFragment() {
......@@ -58,13 +59,14 @@ class ConfidePermissionDialog : DialogFragment() {
private fun reject() {
dismissAllowingStateLoss()
CommonDialog(context)
.setMessage("由于您尚未同意获取麦克风权限,本次通话将使用传统电话方式拨打")
.setRightClick("确定") {
callback?.invoke(false)
}
.setCancelAble(false)
.show()
// CommonDialog(context)
// .setMessage("由于您尚未同意获取麦克风权限,本次通话将使用传统电话方式拨打")
// .setRightClick("确定") {
// callback?.invoke(false)
// }
// .setCancelAble(false)
// .show()
findRouteService(IAudioImService::class.java).openPermissionRejectDialog(activity) { callback?.invoke(false) }
}
override fun onStart() {
......
......@@ -69,6 +69,7 @@ dependencies {
api project(':ydl-webview')
api project(':ydl-platform')
implementation project(":api:im")
implementation project(":api:audioim")
implementation project(":api:user")
implementation project(":api:dynamic")
implementation project(":api:tests")
......
......@@ -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.model.CustomNotification
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.utils.StringUtils
import com.ydl.ydlcommon.utils.remind.HttpErrorUtils
......@@ -440,4 +442,15 @@ class IMServiceImpl : IImService {
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{
private final String KEY_CALLEE="callee";
private final String KEY_DURATION="duration";
private final String KEY_IS_CONSULT_ORDER = "isConsultOrder"; //是否是咨询单,true是
private final String KEY_PULL_CALL = "pullCall"; //是否是咨询单,true是
private String status;
private String caller;
private String callee;
private String duration;
private String isConsultOrder;
private Integer pullCall;//标识是可以拉起通话的 0或空 则不拉起
public CustomAttachConsultCallStatus() {
super(CustomAttachmentType.TYPE_CUSTOMER_CONSULT_CALL_STATUS);
......@@ -28,6 +30,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
this.callee=data.getString(KEY_CALLEE);
this.duration=data.getString(KEY_DURATION);
this.isConsultOrder=data.getString(KEY_IS_CONSULT_ORDER);
this.pullCall = data.getInteger(KEY_PULL_CALL);
}
@Override
......@@ -38,6 +41,7 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
data.put(KEY_CALLEE, callee);
data.put(KEY_DURATION, duration);
data.put(KEY_IS_CONSULT_ORDER, isConsultOrder);
data.put(KEY_PULL_CALL, pullCall);
return data;
}
......@@ -58,4 +62,8 @@ public class CustomAttachConsultCallStatus extends CustomAttachment{
}
public String getIsConsultOrder() {return isConsultOrder;}
public Integer getPullCall() {
return pullCall;
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@ import android.view.View;
import android.widget.ImageView;
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.session.extension.CustomAttachConsultCallStatus;
import com.yidianling.nimbase.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter;
......@@ -17,6 +19,8 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
private ImageView typeImageLeft;
private ImageView typeImageRight;
private TextView statusLabel;
private View lineView;
private TextView btnView;
public MsgViewHolderConsultCallStatus(BaseMultiItemFetchLoadAdapter adapter) {
super(adapter);
......@@ -29,9 +33,11 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
@Override
protected void inflateContentView() {
typeImageLeft = findViewById(R.id.type_img_left);
typeImageRight = findViewById(R.id.type_img_right);
statusLabel = findViewById(R.id.tv_state);
typeImageLeft = findViewById(R.id.message_item_type_img_left);
typeImageRight = findViewById(R.id.message_item_type_img_right);
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
......@@ -57,6 +63,21 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
} else {
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
......@@ -78,5 +99,4 @@ public class MsgViewHolderConsultCallStatus extends MsgViewHolderBase {
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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_content"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/message_item_avchat_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="6dp"
android:minHeight="40dp"
android:orientation="horizontal">
android:orientation="vertical"
android:padding="6dp">
<ImageView
android:id="@+id/type_img_left"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center_vertical"
android:id="@+id/message_item_type_img_left"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:contentDescription="@string/im_empty"
android:src="@drawable/im_avchat_type_audio"
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
android:id="@+id/tv_state"
android:id="@+id/message_item_avchat_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:layout_marginTop="4dp"
android:layout_marginRight="5dp"
android:text="无人接听"
android:textColor="@color/platform_color_242424"
android:textSize="14sp"/>
android:text="无人接听111111111"
android:textColor="@color/color_242424"
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
android:visibility="gone"
android:id="@+id/type_img_right"
android:layout_width="14dp"
android:layout_height="14dp"
android:id="@+id/message_item_type_img_right"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:contentDescription="@string/im_empty"
android:src="@drawable/im_avchat_type_audio"/>
</LinearLayout>
android:src="@drawable/im_avchat_type_audio"
android:visibility="gone"
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 {
// implementation project(":ydl-tuicore")
implementation project(":api:user")
implementation project(":api:course")
implementation project(":api:audioim")
implementation project(":api:im")
implementation project(":api:fm")
implementation project(":api:dynamic")
......
......@@ -2,7 +2,9 @@ package com.yidianling.user
import android.app.Activity
import com.tencent.bugly.crashreport.CrashReport
import com.ydl.audioim.api.IAudioImService
import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.modular.findRouteService
import com.ydl.ydlcommon.utils.ActivityManager
import com.ydl.ydlcommon.utils.BuryPointUtils
import com.ydl.ydlcommon.utils.log.AliYunLogConfig
......@@ -88,6 +90,7 @@ object LoginUtils {
@JvmStatic
fun logout() {
EventBus.getDefault().post(UserLogoutEvent())
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLogout", "用户登出", null)
UserHttpImpl.getInstance().logout(Logout())
.compose(RxUtils.resultData())
.observeOn(AndroidSchedulers.mainThread())
......
......@@ -2,7 +2,9 @@ package com.yidianling.user.ui.login.presenter
import android.annotation.SuppressLint
import android.text.TextUtils
import com.ydl.audioim.api.IAudioImService
import com.ydl.ydlcommon.app.Apm
import com.ydl.ydlcommon.modular.findRouteService
import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.ydl.ydlcommon.router.YdlCommonOut
import com.ydl.ydlcommon.utils.StringUtils
......@@ -105,6 +107,7 @@ class InputPassWordPresenterImpl :
)
var param = PhoneLoginPwdParam(StringUtils.md5(inputPassword), phoneCountryCode, phone)
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLogin", "用户登录", null)
mModel.userLoginByPassword(param)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
......@@ -117,6 +120,7 @@ class InputPassWordPresenterImpl :
ToastUtil.toastShort(it.msg)
Apm.reportEvent("login_android", "password_error", "密码登录失败,$phoneCountryCode-$phone")
mView.startAnim()
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLoginFail", "用户登录失败", null)
} else {
if (it.data.userInfo?.user_type == 2) {
mView.showNormalDialog()
......@@ -135,6 +139,7 @@ class InputPassWordPresenterImpl :
}
mView.closeActivity()
}
findRouteService(IAudioImService::class.java).reportCallEvent("login", "userLoginSuccess", "用户登录成功", null)
AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.LOGIN, "手机号密码 登录成功")
}
......
package com.ydl.ydlcommon.utils.log
import com.apm.insight.log.VLog
import com.ydl.ydlcommon.BuildConfig
object XLog {
fun d(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.d(tag, msg)
}
VLog.i(tag, msg)
}
fun i(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.i(tag, msg)
}
VLog.i(tag, msg)
}
fun w(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.w(tag, msg)
}
VLog.i(tag, msg)
}
fun e(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
android.util.Log.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