Commit 2326a717 by 严久程

咨询加声网

parent 71ff661f
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ydl.audioim">
package="com.ydl.audioim">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.VIBRATE"/>
<application>
<activity
android:name=".AudioHomeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="portrait"/>
android:name=".AudioHomeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="portrait" />
<activity
android:name="com.ydl.consultantim.ConsultantAudioHomeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="portrait" />
</application>
</manifest>
......@@ -70,7 +70,7 @@ import java.util.concurrent.TimeUnit
/**
* @author jiucheng
* @描述: 声网通话页面
* @描述: 倾诉声网通话页面
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/10/30
......@@ -632,13 +632,15 @@ class AudioHomeActivity :
.subscribeOn(Schedulers.computation())
.take(303)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ var result = it.toFloat() / 2.5f
.subscribe({
var result = it.toFloat() / 2.5f
progress_view.setProgress(result)
if (result >= 100f && !iv_hang_up.isEnabled) {
//挂断按钮可点击
iv_hang_up.isEnabled = true
iv_hang_up.setImageResource(R.drawable.audioim_img_hang_up)
}},{},{})
}
}, {}, {})
//开始60s等待倒计时
waitDisposable = Observable.interval(0, 100, TimeUnit.MILLISECONDS)
......@@ -1139,7 +1141,7 @@ class AudioHomeActivity :
ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid!!
var payLoad = PayLoad(channelId ?: "0", time, uid, "1", "999", message)
var connectException = ConnectExceptionCommand(time + zhu, "2", "99", payLoad)
getPresenter().connectException(connectException)
YDLavManager.instances.uploadException(connectException)
}
......
......@@ -9,6 +9,10 @@ import com.ydl.ydl_av.messge_service.callback.InitListener
import com.ydl.ydl_av.messge_service.callback.LoginCallback
import com.ydl.ydl_av.messge_service.request.LoginParam
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.ydl_av.messge_service.callback.CallListener
import com.ydl.ydl_av.messge_service.response.CallLocalResponse
import com.ydl.ydl_av.messge_service.response.CallRemoteResponse
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.log.LogHelper
import io.reactivex.android.schedulers.AndroidSchedulers
......@@ -32,6 +36,71 @@ class YDLavManager {
fun init(context: Context,appId:String){
YDLRTMClient.instances.init(context,appId,listener)
//设置回调
setCallback()
}
fun setCallback() {
YDLRTMClient.instances.setCallListener(object : CallListener {
override fun onCallRecivedByPeer(response: CallLocalResponse?) {
//返回给主叫:被叫已收到呼叫邀请
com.yidianling.common.tools.LogUtil.e("[agora]${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}")
}
override fun onCallAccepted(response: CallLocalResponse?, msg: String?) {
//返回给主叫
com.yidianling.common.tools.LogUtil.e("[agora]${response?.calleeId}已接收呼叫邀请")
}
override fun onCallRefused(response: CallLocalResponse?, msg: String?) {
//返回给主叫
com.yidianling.common.tools.LogUtil.e("[agora]${response?.calleeId}已拒绝呼叫邀请")
}
override fun onCallCanceled(response: CallLocalResponse?) {
//返回给主叫
com.yidianling.common.tools.LogUtil.e("[agora]主叫已取消呼叫邀请")
}
override fun onCallFailure(response: CallLocalResponse?, errorCode: Int) {
//返回给主叫
com.yidianling.common.tools.LogUtil.e("[agora]呼叫${response?.calleeId}用户失败:${response?.response}")
}
override fun onRemoteInvitationReceived(response: CallRemoteResponse?) {
//返回给被叫
com.yidianling.common.tools.LogUtil.e("[agora]收到来自${response?.callerId}的呼叫邀请")
receivedCall(response?.content)
}
override fun onRemoteInvitationAccepted(response: CallRemoteResponse?) {
//返回给被叫
com.yidianling.common.tools.LogUtil.e("[agora]接受来自${response?.callerId}的呼叫成功")
}
override fun onRemoteInvitationRefused(response: CallRemoteResponse?) {
//返回给被叫
com.yidianling.common.tools.LogUtil.e("[agora]已拒绝来自${response?.callerId}的呼叫")
}
override fun onRemoteInvitationCanceled(response: CallRemoteResponse?) {
//返回给被叫
com.yidianling.common.tools.LogUtil.e("[agora]主叫${response?.callerId}已取消呼叫邀请")
closePage(true)
}
override fun onRemoteInvitationFailure(response: CallRemoteResponse?, errorCode: Int) {
//返回给被叫
com.yidianling.common.tools.LogUtil.e("[agora]来自主叫${response?.callerId}的呼叫邀请进程失败:${response?.response}")
//关闭页面
closePage(true)
}
override fun onOtherMsg(error: String?) {
com.yidianling.common.tools.LogUtil.e("[agora]其它消息:${error}")
}
})
}
@SuppressLint("CheckResult")
......@@ -86,6 +155,22 @@ class YDLavManager {
}
/**
* RTM登录异常,上传错误日志 msg
* 声网出现异常,上传错误日志 connectException
*/
@SuppressLint("CheckResult")
fun uploadException(connectException: ConnectExceptionCommand) {
AudioApiRequestUtil.connectException(connectException)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
}, {
com.yidianling.common.tools.LogUtil.e("agora", "声网上传异常与错误日志接口调用失败:" + it.message)
})
}
/**
* 实时消息全局监听
*/
private val listener = object :InitListener{
......
......@@ -71,11 +71,6 @@ interface IAudioHomeActivityContract {
*/
// fun connectFinish(param: ConnectFinishCommand)
/**
* 通话异常
*/
fun connectException(param: ConnectExceptionCommand)
/**
* 通知服务端发送推送
......
......@@ -2,6 +2,7 @@ package com.ydl.audioim.http
import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.ConnectBean
import com.ydl.consultantim.bean.ListenTokenBean
import com.ydl.ydlcommon.base.config.YDL_DOMAIN
import com.ydl.ydlcommon.base.config.YDL_DOMAIN_JAVA
import com.ydl.ydlcommon.data.http.BaseAPIResponse
......@@ -61,4 +62,10 @@ interface AudioNetAPi {
@GET("im/getAgoraToken")
fun getAgoraToken(): Observable<BaseAPIResponse<AgoraTokenResponse>>
//获取token
@FormUrlEncoded
@POST("listen/token")
fun listenToken(@FieldMap map: Map<String, String>): Observable<BaseResponse<ListenTokenBean>>
}
\ No newline at end of file
......@@ -66,20 +66,6 @@ class AudioHomePresenterImpl : BasePresenter<IAudioHomeActivityContract.View, IA
// })
// }
@SuppressLint("CheckResult")
override fun connectException(param: ConnectExceptionCommand) {
mModel.connectException(param)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
}
.doAfterTerminate {
}
.subscribe({
}, { e ->
LogUtil.e(e.message)
})
}
/**
* 登录声网
......
package com.ydl.consultantim
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Paint
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings
import android.text.TextUtils
import android.view.View
import android.view.animation.AccelerateInterpolator
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.google.gson.Gson
import com.tbruyelle.rxpermissions2.RxPermissions
import com.ydl.audioim.BuildConfig
import com.ydl.audioim.R
import com.ydl.audioim.YDLavManager
import com.ydl.audioim.http.command.ConnectExceptionCommand
import com.ydl.audioim.http.command.PayLoad
import com.ydl.audioim.player.AudioPlayer
import com.ydl.audioim.utils.AudioLogUtils
import com.ydl.consultantim.contract.IConsultantAudioHomeActivityContract
import com.ydl.consultantim.event.AudioHomeEvent
import com.ydl.consultantim.presenter.ConsultantAudioHomePresenterImpl
import com.ydl.consultantim.utils.ConsultantAudioUtils
import com.ydl.consultantim.utils.VibratorUtil
import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.ydl_av.messge_service.YDLRTMClient
import com.ydl.ydl_av.voice.listener.IYDLVoiceEventHandler
import com.ydl.ydl_av.voice.manager.YDLVoiceManager
import com.ydl.ydl_image.config.SimpleImageOpConfiger
import com.ydl.ydl_image.manager.YDLImageCacheManager
import com.ydl.ydl_router.manager.YDLRouterManager
import com.ydl.ydlcommon.base.BaseMvpActivity
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.router.YdlCommonRouterManager
import com.ydl.ydlcommon.utils.ActivityManager
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.StatusBarUtils
import com.ydl.ydlcommon.utils.Utils
import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.ydl.ydlcommon.utils.log.LogHelper
import com.ydl.ydlcommon.utils.remind.ToastHelper
import com.ydl.ydlcommon.view.dialog.CommonDialog
import com.yidianling.common.tools.RxActivityTool
import com.yidianling.common.tools.ToastUtil
import com.yidianling.user.api.service.IUserService
import de.greenrobot.event.EventBus
import io.agora.rtc.IRtcEngineEventHandler
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.audioim_cativity_consultant_audio_home.*
import org.reactivestreams.Subscription
/**
* @author jiucheng
* @描述: 咨询声网通话页面
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/10/30
*/
@Route(path = "/av/AudioHomeActivity")
class ConsultantAudioHomeActivity :
BaseMvpActivity<IConsultantAudioHomeActivityContract.View, IConsultantAudioHomeActivityContract.Presenter>(),
IConsultantAudioHomeActivityContract.View,
SensorEventListener {
//语音管理类
private var voiceManage: YDLVoiceManager? = null
//音视频数据
private var mAudioMessageBean: AudioMessageBean? = null
//音频播放
private var mPlayer: AudioPlayer? = null
//当前状态 0.未接听 1.已接听
public var status = STATUS_NOT_ANSWERED
//电源管理对象
private var localPowerManager: PowerManager? = null
//电源锁
private var localWakeLock: PowerManager.WakeLock? = null
private var sensorManager: SensorManager? = null
/**
* 通话开始时间(接通)
*/
private var callStartTime: Long? = null
/**
* 本次倾述倒计时
*/
private var totalDisposable: Disposable? = null
private var ensureDialog: CommonDialog? = null
//频道管理器
// private var channelManager: ChannelManager? = null
//频道token
private var channelToken: String? = null
private var hasUpLoadLog = false
private var dialog: CommonDialog? = null
/**
* 事件回调 (SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等)
*/
private val mRtcEventHandler = object : IYDLVoiceEventHandler() {
/**
* 远端用户静音回调
* @param uid 用户 ID
* @param muted 该用户是否静音:true: 该用户已静音音频 false: 该用户已取消音频静音
*/
override fun onUserMuteAudio(uid: Int, muted: Boolean) {
runOnUiThread {
showLongToast("[agora]对方静音了,提醒他打开!")
}
}
override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
super.onJoinChannelSuccess(channel, uid, elapsed)
LogUtil.e("[agora]$uid 加入频道回调")
// 加入频道后再通知用户已接受
YDLRTMClient.instances.acceptCall(mAudioMessageBean?.channelId)
runOnUiThread {
tv_toast.visibility = View.VISIBLE
tv_toast.text = "连接中..."
}
}
override fun onUserJoined(uid: Int, elapsed: Int) {
super.onUserJoined(uid, elapsed)
LogUtil.e("[agora]远端用户加入频道回调")
//另一方加入频道成功
runOnUiThread {
//通话开始,刷新ui开始倒计时
updateStartUi()
status = STATUS_ANSWERED
//接通开始回调
callStartTime = System.currentTimeMillis()
}
}
override fun onRtcStats(stats: IRtcEngineEventHandler.RtcStats?) {
if (null != stats?.users && stats.users == 1) {
ToastUtil.toastShort("用户已挂断")
//通话结束或挂断时,上传日志文件
uploadLog()
leaveChannel()
}
}
override fun onConnectionStateChanged(state: Int, reason: Int) {
super.onConnectionStateChanged(state, reason)
// 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑
if (reason == 3) {
ToastUtil.toastShort("用户已挂断")
//通话结束或挂断时,上传日志文件
uploadLog()
leaveChannel()
}
}
/**
* 离开频道回调(自己)
*
*/
override fun onLeaveChannel(stats: IRtcEngineEventHandler.RtcStats?) {
super.onLeaveChannel(stats)
LogUtil.e("[agora]自己离开频道回调")
//通话结束或挂断时,上传日志文件
uploadLog()
runOnUiThread {
//通知php 通话已结束
close(RESULT_ANSWERED_CODE, "")
}
}
/**
* 主播离开频道回调
* 提示有主播离开了频道(或掉线)。
* SDK 判断用户离开频道(或掉线)的依据是超时:在一定时间内(20 秒)没有收到对方的任何数据包,判定为对方掉线。
* 在网络较差的情况下,可能会有误报。建议可靠的掉线检测应该由信令来做
* @param uid 主播 ID
* @param reason 离线原因:
* USER_OFFLINE_QUIT(0):用户主动离开
* USER_OFFLINE_DROPPED(1):因过长时间收不到对方数据包,超时掉线。注意:由于 SDK 使用的是不可靠通道,也有可能对方主动离开本方没收到对方离开消息而误判为超时掉线
* USER_OFFLINE_BECOME_AUDIENCE(2):用户身份从主播切换为观众(直播模式下)
*/
override fun onUserOffline(uid: Int, elapsed: Int) {
super.onUserOffline(uid, elapsed)
LogUtil.e("[agora]$uid 主播离开频道回调")
runOnUiThread {
showLongToast("用户已挂断")
//通话结束或挂断时,上传日志文件
uploadLog()
if (null != totalDisposable) {
totalDisposable!!.dispose()
}
//通知php 通话已结束
close(RESULT_ANSWERED_CODE, "")
}
}
/**
* 重新加入频道回调
*/
override fun onRejoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
super.onRejoinChannelSuccess(channel, uid, elapsed)
LogUtil.e("[agora]$uid 重新加入频道回调")
}
/**
* 网络质量报告回调
* 报告本地用户的网络质量。当你调用 enableLastmileTest 之后,该回调函数每 2 秒触发一次
*/
override fun onLastmileQuality(quality: Int) {
super.onLastmileQuality(quality)
}
override fun onWarning(warn: Int) {
super.onWarning(warn)
// 过滤1031 录制声音模糊
if (warn != 1031) {
uploadException("mRtcEventHandler-onWarning:warnCode--%${warn}")
}
LogUtil.e("[agora]发生警告回调=$warn")
//103:没有可用的频道资源。可能是因为服务端没法分配频道资源
//104:查找频道超时。在加入频道时 SDK 先要查找指定的频道,出现该警告一般是因为网络太差,连接不到服务器
//105:查找频道请求被服务器拒绝。服务器可能没有办法处理这个请求或请求是非法的
//106:打开频道超时。查找到指定频道后,SDK 接着打开该频道,超时一般是因为网络太差,连接不到服务器
//107:打开频道请求被服务器拒绝。服务器可能没有办法处理该请求或该请求是非法的
runOnUiThread {
when (warn) {
103, 104, 105, 106, 107 -> {
showLongToast("当前网络较差,请更换网络!")
//通话结束或挂断时,上传日志文件
uploadLog()
close(RESULT_NOT_ANSWERED_CODE, "[agora]专家网络较差")
}
}
}
}
override fun onError(err: Int) {
super.onError(err)
uploadException("mRtcEventHandler-onError:errorCode--%${err}")
LogUtil.e("[agora] 发生错误回调 =$err")
//3:SDK 初始化失败。Agora 建议尝试以下处理方法
//7:SDK 尚未初始化,就调用其 API。请确认在调用 API 之前已创建 RtcEngine 对象并完成初始化
//10:API 调用超时。有些 API 调用需要 SDK 返回结果,如果 SDK 处理时间过长,超过 10 秒没有返回,会出现此错误
//17:加入频道被拒绝。一般有以下原因:
//用户已进入频道,再次调用加入频道的 API,例如 joinChannel,会返回此错误。停止调用该方法即可。
//用户在做 Echo 测试时尝试加入频道。等待 Echo test 结束后再加入频道即可。
//101:不是有效的 APP ID。请更换有效的 APP ID 重新加入频道
//102:不是有效的 频道名。请更换有效的频道名重新加入频道
//109:当前使用的 Token 过期,不再有效
//110:生成的 Token 无效
//123:此用户被服务器禁止
runOnUiThread {
when (err) {
3, 7, 109, 110 -> {
showLongToast("请退出应用,重新打开")
close(RESULT_NOT_ANSWERED_CODE, "咨询师已挂断")
}
10, 17 -> {
showLongToast("当前网络较差,请更换网络")
close(RESULT_NOT_ANSWERED_CODE, "专家网络较差")
}
101 -> {
showLongToast("安装包有问题,请联系技术")
close(RESULT_NOT_ANSWERED_CODE, "安装包有问题,请联系技术")
}
102 -> {
showLongToast("频道错误,请联系技术")
close(RESULT_NOT_ANSWERED_CODE, "频道错误,请联系技术")
}
123 -> {
showLongToast("当前用户不允许接听电话,请联系客服")
close(RESULT_NOT_ANSWERED_CODE, "该专家不允许接听电话,请联系客服")
}
else -> {
}
}
}
}
}
override fun createPresenter(): IConsultantAudioHomeActivityContract.Presenter {
return ConsultantAudioHomePresenterImpl()
}
override fun layoutResId(): Int {
return R.layout.audioim_cativity_consultant_audio_home
}
companion object {
const val PARAM: String = "param"
//0.未接听 1.已接听
const val STATUS_NOT_ANSWERED = 0
const val STATUS_ANSWERED = 1
//666.未接听,直接挂断 667.已接听,正常挂断 668:未接听,用户端取消了
const val RESULT_NOT_ANSWERED_CODE = 666
const val RESULT_ANSWERED_CODE = 667
const val RESULT_USER_CANCEL = 668
}
override fun initDataAndEvent() {
EventBus.getDefault().register(this)
//状态栏颜色
setWindowStatusBarColor()
//点亮屏幕 并解锁
ConsultantAudioUtils.wakeUpAndUnlock(this)
//页面传递数据初始化
getParam()
//设置信令的回调
setCallback()
//初始化传感器
initSensorManager()
initUser()
initData()
//获取声网频道号
getChannelToken()
}
private fun setWindowStatusBarColor() {
StatusBarUtils.setWindowStatusBarColor(this, R.color.audioim_color_40353535)
}
private fun getParam() {
if (null == intent) {
close(RESULT_NOT_ANSWERED_CODE, "通话异常")
return
}
if (null != intent.getStringExtra(PARAM)) {
val json = intent.getStringExtra(PARAM)
if (!TextUtils.isEmpty(json)) {
writeAgoraLog(json)
mAudioMessageBean = Gson().fromJson(json, AudioMessageBean::class.java)
}
} else {
close(RESULT_NOT_ANSWERED_CODE, "通话异常")
return
}
}
private fun setCallback() {
/**
* RTC 回调 在[YDLavManager.setCallback]中注册使用
*/
// YDLRTMClient.instances.setCallListener(object : CallListener {
// override fun onCallRecivedByPeer(response: CallLocalResponse?) {
// //返回给主叫:被叫已收到呼叫邀请
// LogUtil.I("[agora]${response?.calleeId}已收到呼叫邀请,频道号${response?.ChannelId}")
// }
//
// override fun onCallAccepted(response: CallLocalResponse?, msg: String?) {
// //返回给主叫
// LogUtil.I("[agora]${response?.calleeId}已接受呼叫邀请")
// }
//
// override fun onCallRefused(response: CallLocalResponse?, msg: String?) {
// //返回给主叫
// LogUtil.I("[agora]${response?.calleeId}已拒绝呼叫邀请")
// }
//
// override fun onCallCanceled(response: CallLocalResponse?) {
// //返回给主叫
// LogUtil.I("[agora]主叫已取消呼叫邀请")
// }
//
// override fun onCallFailure(response: CallLocalResponse?, errorCode: Int) {
// //返回给主叫
// LogUtil.I("[agora]呼叫${response?.calleeId}用户失败:${response?.response}")
//
// }
//
// override fun onRemoteInvitationReceived(response: CallRemoteResponse?) {
// //返回给被叫
// LogUtil.I("[agora]收到来自${response?.callerId}的呼叫邀请")
// }
//
// override fun onRemoteInvitationAccepted(response: CallRemoteResponse?) {
// //返回给被叫
// LogUtil.I("[agora]接受来自${response?.callerId}的呼叫成功")
// }
//
// override fun onRemoteInvitationRefused(response: CallRemoteResponse?) {
// //返回给被叫
// LogUtil.I("[agora]已拒绝来自${response?.callerId}的呼叫")
// }
//
// override fun onRemoteInvitationCanceled(response: CallRemoteResponse?) {
// //返回给被叫
// LogUtil.I("[agora]主叫${response?.callerId}已取消呼叫邀请")
// }
//
// override fun onRemoteInvitationFailure(response: CallRemoteResponse?, errorCode: Int) {
// //返回给被叫
// LogUtil.I("[agora]来自主叫${response?.callerId}的呼叫邀请进程失败:${response?.response}")
// }
//
// override fun onOtherMsg(error: String?) {
// LogUtil.I("[agora]其它消息:${error}")
// }
// })
// channelManager = YDLRTMClient.instances.createChannelManager(mAudioMessageBean?.channelId, object : ChannelListener {
// override fun onMemberCountUpdated(memberCount: Int) {
// //频道人数更新
// com.yidianling.common.tools.LogUtil.i("[agora]当前频道人数:$memberCount")
// }
//
// override fun onMessageReceived(message: RTMMesssage?, member: ChannelMember?) {
// //接到频道消息
// com.yidianling.common.tools.LogUtil.i("[agora]接到${member?.channelId}频道${member?.userId}的消息:" + message?.text)
// }
//
// override fun onMemberJoined(member: ChannelMember?) {
// //新用户加入频道
// com.yidianling.common.tools.LogUtil.i("[agora]新用户加入${member?.channelId}频道:${member?.userId}")
// }
//
// override fun onMemberLeft(member: ChannelMember?) {
// com.yidianling.common.tools.LogUtil.i("[agora]有用户离开${member?.channelId}频道:${member?.userId}")
// }
// })
}
@SuppressLint("InvalidWakeLockTag")
private fun initSensorManager() {
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager?
localPowerManager = getSystemService(POWER_SERVICE) as PowerManager?
localWakeLock = localPowerManager!!.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
"yidianling"
)
}
/**
* 初始化界面用户信息
*/
private fun initUser() {
var userName = "倾诉用户"
if (null != mAudioMessageBean) {
if (!TextUtils.isEmpty(mAudioMessageBean!!.userName)) {
userName = mAudioMessageBean!!.userName!!
}
if (!TextUtils.isEmpty(mAudioMessageBean!!.userIcon)) {
var option = SimpleImageOpConfiger()
option.errorPic = R.drawable.audioim_head_place_hold_pic
option.loadingPic = R.drawable.audioim_head_place_hold_pic
option.transform = 0
YDLImageCacheManager.showImage(this, mAudioMessageBean!!.userIcon, iv_head, option)
} else {
iv_head.setBackgroundResource(R.drawable.audioim_head_place_hold_pic)
}
} else {
iv_head.setBackgroundResource(R.drawable.audioim_head_place_hold_pic)
}
tv_name.text = userName
}
private fun initData() {
wave_view.setDuration(6000)
wave_view.setStyle(Paint.Style.STROKE)
wave_view.setSpeed(1000)
wave_view.setColor(Color.WHITE)
wave_view.setInitialRadius(140f)
wave_view.setInterpolator(AccelerateInterpolator(1.2f))
//启动背景动画
wave_view.start()
mPlayer = AudioPlayer(this)
mPlayer!!.setDataSource(R.raw.audioim_call_music)
mPlayer!!.start(isLooping = true, isSetOnCompletionListener = false)
//间接性震动手机
VibratorUtil.vibrate(AudioHomeActivity@ this, longArrayOf(1000, 1000, 1000, 1000), true)
}
/**
* //获取声网频道号
*/
private fun getChannelToken() {
//获取频道token
mPresenter.getChannelToken(mAudioMessageBean, false)
}
override fun channelTokenResponse(token: String?, needJoinChannel: Boolean) {
if (TextUtils.isEmpty(token)) {
LogUtil.e("[agora]token not null")
ToastUtil.toastShort("通话频道不存在")
finish()
return
}
this.channelToken = token
if (needJoinChannel) {
//权限申请
requestPermission()
}
}
//申请音频权限
@SuppressLint("CheckResult")
private fun requestPermission() {
val rxPermissions = RxPermissions(this)
rxPermissions.requestEach(Manifest.permission.RECORD_AUDIO)
.subscribe { permission ->
when {
//权限已申请 进行初始化操作
permission.granted -> init()
//权限为申请 重新申请
permission.shouldShowRequestPermissionRationale -> requestPermission()
//跳转设置界面
else -> {
ToastHelper.show(getString(R.string.audioim_need_storage_permission_hint))
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
finish()
}
}
}
}
private fun init() {
//初始化语音
initializeAgoraEngine()
joinChannel()
}
/**
* 语音通话 初始化
*/
private fun initializeAgoraEngine() {
/**
* 创建一个实例
* param appId 应用id
* param mRtcEventHandler 事件回调(SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等)
*/
voiceManage = YDLVoiceManager(this, BuildConfig.AGORA_APPID, mRtcEventHandler)
voiceManage!!.init()
//默认听筒模式
voiceManage!!.getVoiceApi().setEnableSpeakerphone(false)
}
/**
* 更新ui
*/
private fun updateUI() {
rl_call.visibility = View.GONE
rl_hands_free.visibility = View.VISIBLE
}
/**
* 加入频道
*/
private fun joinChannel() {
val account = YdlCommonRouterManager.getYdlCommonRoute().getUid()
if (!TextUtils.isEmpty(mAudioMessageBean?.channelId)) {
LogUtil.e("[agora] joinChannel:$account")
voiceManage?.getVoiceApi()?.joinChannel(
channelToken
?: "", mAudioMessageBean!!.channelId!!, "Extra Optional Data", account
)
}
}
override fun executeFinish() {
//通话结束或挂断时,上传日志文件
uploadLog()
ToastUtil.toastShort("用户已挂断")
close(RESULT_ANSWERED_CODE, "")
}
/**
* 通话开始后更新ui
*/
private fun updateStartUi() {
tv_toast.text = "已接通"
tv_toast.postDelayed({ tv_toast.visibility = View.GONE }, 500)
rl_remain_time.visibility = View.VISIBLE
}
/**
* 通话结束
*/
private fun callFinish() {
status = STATUS_NOT_ANSWERED
}
/**
* 显示终止服务弹窗
*/
private fun showStopService() {
if (null == dialog) {
dialog = CommonDialog(this)
.setTitle("温馨提示")
.setMessage("中途挂断可能会影响您在倾听专家榜的排名和曝光率,要谨慎选择哦!")
.setLeftOnclick("点错了", null)
.setRightClick("挂断") {
if (status == STATUS_NOT_ANSWERED) {
//通话结束或挂断时,上传日志文件
uploadLog()
//当未接听 直接挂断 要发送给用户一条消息
close(RESULT_NOT_ANSWERED_CODE, "咨询师已挂断")
} else if (status == STATUS_ANSWERED) {
//正常接听 挂断电话 需要重置信令管理类状态
close(RESULT_ANSWERED_CODE, "")
}
}
.setCancelAble(true)
}
if (null != dialog && !isFinishing) {
dialog!!.show()
}
}
override fun listenStatusPushResponse() {
}
/**
* 挂断
*/
fun hangUpClick(view: View) {
if (Utils.isFastClick()) {
//防止连击
return
}
showStopService()
ActionCountUtils.count(
"shengwang_popup_layer_page|shengwang_popup_layer_refuse_click",
YdlCommonRouterManager.getYdlCommonRoute().getUid().toString(),
uid = YdlCommonRouterManager.getYdlCommonRoute().getUid().toString()
)
}
/**
* 接听
*/
fun onCall(view: View) {
if (Utils.isFastClick()) {
//防止连击
return
}
//调用接口判断邀请专家的用户当前是否在频道内,如果在,则专家加入频道,如果不在,则提示用户已挂断
//更改逻辑:现在是专家先进频道,这个接口暂时只作为参考
// presenter.userIsInChannel(mAudioMessageBean?.channelId ?: "", mAudioMessageBean?.userId
// ?: "")
executeCall(true)
ActionCountUtils.count(
"shengwang_popup_layer_page|shengwang_popup_layer_answer_click",
YdlCommonRouterManager.getYdlCommonRoute().getUid().toString(),
uid = YdlCommonRouterManager.getYdlCommonRoute().getUid().toString()
)
}
/**
* 执行接听操作
*/
override fun executeCall(canExcute: Boolean) {
stopPlaying()
//停止震动
VibratorUtil.StopVibrate(this)
if (!canExcute) {
ToastUtil.toastShort("用户已挂断")
finish()
} else {
if (null != mAudioMessageBean) {
updateUI()
//专家先加入频道,然后再接受用户邀请,保证专家比用户先加入频道
if (!TextUtils.isEmpty(channelToken)) {
//权限申请完成后加入频道
requestPermission()
} else {
//获取频道token
mPresenter.getChannelToken(mAudioMessageBean)
}
}
}
}
/**
* 打开扬声器 true:开启 false:关闭(听筒)
*/
fun onSwitchSpeakerphoneClicked(view: View) {
view.isSelected = !view.isSelected
if (view.isSelected) {
//扬声器模式
iv_hands_free.setImageResource(R.drawable.audioim_img_hands_free)
} else {
//听筒模式
iv_hands_free.setImageResource(R.drawable.audioim_img_hands_free_unuse)
}
LogUtil.e("http---------------isSelected=" + !view.isSelected)
if (null == voiceManage || null == voiceManage!!.getVoiceApi()) {
return
}
voiceManage!!.getVoiceApi().setEnableSpeakerphone(view.isSelected)
}
override fun onResume() {
super.onResume()
sensorManager!!.registerListener(
this,
sensorManager!!.getDefaultSensor(Sensor.TYPE_PROXIMITY),
SensorManager.SENSOR_DELAY_NORMAL
)
ActionCountUtils.count(
"shengwang_popup_layer_page|shengwang_popup_layer_page_visit",
"",
uid = YdlCommonRouterManager.getYdlCommonRoute().getUid().toString()
)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
}
override fun onSensorChanged(event: SensorEvent?) {
val values = event!!.values
when (event.sensor.type) {
Sensor.TYPE_PROXIMITY -> {
if (values[0] == 0.0f) {
//贴近手机
if (!localWakeLock!!.isHeld) {
localWakeLock!!.acquire()
}
} else {
//离开手机
//唤醒设备
if (localWakeLock!!.isHeld) {
localWakeLock!!.release()
}
}
}
}
}
private fun showLongToast(msg: String) {
ToastUtil.toastLong(AudioHomeActivity@ this, msg)
}
private fun showShortToast(msg: String) {
ToastUtil.toastLong(AudioHomeActivity@ this, msg)
}
//关闭本页面
fun close(code: Int, msg: String) {
LogUtil.e("[agora]close(code:$code,msg:$msg)")
if (status == STATUS_ANSWERED) {
//不再做操作,原先为调用接口
}
stopMusic()
//播放结束音频
playFinishMusic()
if (code == RESULT_ANSWERED_CODE) {
//已接听,正常挂断
//离开频道
leaveChannel()
} else if (code == RESULT_NOT_ANSWERED_CODE) {
//未接听,直接挂断 发送消息
YDLRTMClient.instances.refuseCall(mAudioMessageBean?.channelId)
} else if (code == RESULT_USER_CANCEL) {
// ToastUtils.toastShort(this, "用户已挂断")
}
finish()
}
private fun stopMusic() {
//停止播放音乐
stopPlaying()
//停止震动
VibratorUtil.StopVibrate(AudioHomeActivity@ this)
}
/**
* 停止播放
*/
private fun stopPlaying() {
if (mPlayer != null) {
mPlayer!!.pause()
}
}
/**
* 播放结束音频
*/
private fun playFinishMusic() {
if (mPlayer == null) {
mPlayer = AudioPlayer(this)
}
mPlayer!!.setDataSource(R.raw.audioim_hand_down_music)
// mPlayer!!.switchPlayType(true)
mPlayer!!.start(isLooping = false, isSetOnCompletionListener = false)
}
fun onEventMainThread(event: AudioHomeEvent) {
stopMusic()
}
/**
* 离开频道
*/
private fun leaveChannel() {
uploadException("", "zhu", "108")
if (totalDisposable != null) {
totalDisposable!!.dispose()
}
if (null != voiceManage && null != voiceManage!!.getVoiceApi()) {
voiceManage!!.getVoiceApi().leaveChannel()
voiceManage!!.getVoiceApi().destroy()
voiceManage = null
}
if (mPlayer != null) {
mPlayer!!.clear()
}
if (null != sensorManager) {
sensorManager!!.unregisterListener(this)
}
//唤醒设备
if (null != localWakeLock && localWakeLock!!.isHeld) {
localWakeLock!!.release()
}
sensorManager = null
localWakeLock = null
localPowerManager = null
}
private fun voiceDestory() {
if (null != voiceManage && null != voiceManage!!.getVoiceApi()) {
voiceManage!!.getVoiceApi().destroy()
}
voiceManage = null
}
/**
* 重写返回键逻辑:屏蔽返回键
*/
override fun onBackPressed() {
}
private fun writeAgoraLog(content: String) {
Observable.create<Any> {
try {
AudioLogUtils.writeAgoraLog(content)
} catch (e: Exception) {
}
}.subscribeOn(Schedulers.io())
.subscribe()
}
/**
* 上传错误日志
*/
private fun uploadException(message: String, zhu: String = "", eventType: String = "99") {
var time: String = (System.currentTimeMillis() / 1000).toString()
var uid: String =
ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid!!
var payLoad = PayLoad(mAudioMessageBean?.channelId ?: "0", time, uid, "1", "999", message)
var connectException = ConnectExceptionCommand(time + zhu, "2", eventType, payLoad)
YDLavManager.instances.uploadException(connectException)
}
private fun uploadLog() {
if (!hasUpLoadLog) {
hasUpLoadLog = true
LogHelper.getInstance().uploadLog(false)
}
}
fun showEnsureDialog() {
runOnUiThread {
//停止震动
VibratorUtil.StopVibrate(this)
if (ensureDialog == null) {
ensureDialog = CommonDialog(this)
.setMessage("用户已结束通话")
.setRightClick("确定") {
finish()
}
.setCancleIsVisibility(View.GONE)
.setCancelAble(false)
}
if (null != ensureDialog && !isFinishing) {
ensureDialog!!.show()
}
}
}
override fun onDestroy() {
LogUtil.e("http-------------onDestory")
leaveChannel()
voiceDestory()
status = STATUS_NOT_ANSWERED
hasUpLoadLog = false
EventBus.getDefault().unregister(this)
if (ActivityManager.getActivitySize() == 1) {
try {
// startActivity(MainActivity.newIntent(this, false))
ARouter.getInstance().build("/main/main")
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.navigation()
} catch (e: Exception) {
}
}
super.onDestroy()
}
}
package com.ydl.consultantim.bean
/**
* @author yuanWai
* @描述:
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/11/9
*/
data class ListenTokenBean(val token : String?,
val expired : String?,
val type : String?)
\ No newline at end of file
package com.ydl.consultantim.command
import com.ydl.ydlcommon.data.http.BaseCommand
/**
* @author jiucheng
* @描述:
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2020/3/19
*/
data class ListenTokenCmd(
/**
* 类别 TOKEN|SIGNAL_TOKEN
*/
var type: String? = null,
/**
* 账号(就是uid)
* 如果是SIGNAL_TOKEN 则需要传递该参数
*/
var account: String? = null,
/**
* 频道ID
*/
var channelId: String? = null,
/**
* 过期时间
*/
var expired: String? = null
) : BaseCommand()
\ No newline at end of file
package com.ydl.consultantim.contract
import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.ydlcommon.mvp.base.IModel
import com.ydl.ydlcommon.mvp.base.IPresenter
import com.ydl.ydlcommon.mvp.base.IView
/**
* @author jiucheng
* @描述:声网倾诉首页约束类
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/10/30
*/
interface IConsultantAudioHomeActivityContract {
interface View : IView {
fun listenStatusPushResponse()
fun channelTokenResponse(token: String?,needJoinChannel: Boolean)
// 执行接听操作
// canExcute是否执行
fun executeCall(canExcute: Boolean)
// 关闭当前页面,并提示用户已挂断
fun executeFinish()
}
interface Presenter : IPresenter<View> {
/**
* 通话开始(更新专家状态)
* @param param 请求参数
* @param isSwitchAxb 是否切换axb
*/
// fun connectStart(param: ConnectStartCommand)
/**
* 通话结束(更新专家状态)
* @param param 请求参数
* @param isSwitchAxb 是否切换axb
*/
// fun connectFinish(param: ConnectFinishCommand)
/**
* 获取频道token
*
*/
fun getChannelToken(mAudioMessageBean: AudioMessageBean?, needJoinChannel: Boolean = true)
/**
* 判断用户是否在频道中
*/
fun userIsInChannel(channelName: String, agoraUid: String)
}
interface Model : IModel {
/**
* 通话开始回调(更新专家状态)
*/
// fun connectStart(param: ConnectStartCommand)
/**
* 通话结束(更新专家状态)
*/
// fun connectFinish(param: ConnectFinishCommand)
}
}
package com.ydl.consultantim.event;
/**
* @author yuanWai
* @描述:
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/11/19
*/
public class AudioHomeEvent {
//1.发送停止播放电话铃声和震动
public int type;
public AudioHomeEvent(int type){
this.type = type;
}
}
package com.ydl.consultantim.model
import com.ydl.consultantim.contract.IConsultantAudioHomeActivityContract
/**
* @author jiucheng
* @描述:声网通话页面数据实现类
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/10/30
*/
class ConsultantAudioHomeModelImpl : IConsultantAudioHomeActivityContract.Model {
}
package com.ydl.consultantim.presenter
import com.ydl.consultantim.command.ListenTokenCmd
import com.ydl.consultantim.contract.IConsultantAudioHomeActivityContract
import com.ydl.consultantim.model.ConsultantAudioHomeModelImpl
import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.yidianling.user.api.service.IUserService
/**
* @author jiucheng
* @描述:声网通话页面逻辑实现类
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/10/30
*/
class ConsultantAudioHomePresenterImpl :
BasePresenter<IConsultantAudioHomeActivityContract.View, IConsultantAudioHomeActivityContract.Model>(),
IConsultantAudioHomeActivityContract.Presenter {
override fun userIsInChannel(channelName: String, agoraUid: String) {
// RetrofitUtils.userIsInChannel(channelName, agoraUid)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe({
// view.executeCall(it.data)
// }, {
// LogUtil.e("agora", "专家进入前判断用户是否在频道内接口异常:" + it.message)
// })
}
override fun getChannelToken(msgBean: AudioMessageBean?, needJoinChannel: Boolean) {
if (ModularServiceManager.provide(IUserService::class.java).isLogin()) {
if (null != ModularServiceManager.provide(IUserService::class.java).getUserInfo()) {
val acount =
ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid
val tokenParam = ListenTokenCmd()
tokenParam.type = "TOKEN"
tokenParam.account = acount.toString()
tokenParam.uid = acount.toString()
tokenParam.channelId = msgBean?.channelId
//todo
// RetrofitUtils.listenToken(tokenParam)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe({
// view.channelTokenResponse(it.data.token, needJoinChannel)
// }, {
// RetrofitUtils.handleError(it)
// })
}
}
}
override fun createModel(): IConsultantAudioHomeActivityContract.Model {
return ConsultantAudioHomeModelImpl()
}
}
package com.ydl.consultantim.utils
import android.annotation.SuppressLint
import android.app.KeyguardManager
import android.content.Context
import android.os.PowerManager
/**
* @author jiucheng
* @描述:
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2020/3/19
*/
class ConsultantAudioUtils {
companion object{
/**
* 唤醒手机屏幕并解锁
*/
@SuppressLint("InvalidWakeLockTag")
fun wakeUpAndUnlock(context: Context) { // 获取电源管理器对象
val pm = context.applicationContext
.getSystemService(Context.POWER_SERVICE) as PowerManager
val screenOn = pm.isScreenOn
if (!screenOn) { // 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是LogCat里用的Tag
val wl = pm.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP or
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright"
)
wl.acquire(10000) // 点亮屏幕
wl.release() // 释放
}
// 屏幕解锁
val keyguardManager =context.applicationContext
.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
val keyguardLock = keyguardManager.newKeyguardLock("unLock")
// 屏幕锁定
keyguardLock.reenableKeyguard()
keyguardLock.disableKeyguard() // 解锁
}
}
}
\ No newline at end of file
package com.ydl.consultantim.utils;
import android.app.Activity;
import android.app.Service;
import android.os.Vibrator;
/**
* @author yuanWai
* @描述:震动工具类
* @Copyright Copyright (c) 2018
* @Company 壹点灵
* @date 2018/11/16
*/
public class VibratorUtil {
/**
* final Activity activity :调用该方法的Activity实例 long milliseconds :震动的时长,单位是毫秒
* long[] pattern :自定义震动模式 。数组中数字的含义依次是[静止时长,震动时长,静止时长,震动时长。。。]时长的单位是毫秒
* boolean isRepeat : 是否反复震动,如果是true,反复震动,如果是false,只震动一次
*/
// 一直震动多少秒
public static void vibrate(final Activity activity, long milliseconds) {
Vibrator vib = (Vibrator) activity
.getSystemService(Service.VIBRATOR_SERVICE);
assert vib != null;
vib.vibrate(milliseconds);
}
// 按照我们传进去的数组进行间歇性的震动
public static void vibrate(final Activity activity, long[] pattern,
boolean isRepeat) {
Vibrator vib = (Vibrator) activity
.getSystemService(Service.VIBRATOR_SERVICE);
assert vib != null;
vib.vibrate(pattern, isRepeat ? 1 : -1);
}
// 停止震动
public static void StopVibrate(final Activity activity) {
Vibrator vib = (Vibrator) activity
.getSystemService(Service.VIBRATOR_SERVICE);
assert vib != null;
vib.cancel();
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/platform_color_80353535">
<RelativeLayout
android:id="@+id/rl_head"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="74dp">
<com.ydl.ydlcommon.view.WaveView
android:id="@+id/wave_view"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true" />
<ImageView
android:layout_width="126dp"
android:layout_height="126dp"
android:layout_centerInParent="true"
android:background="@drawable/audioim_head_background" />
<ImageView
android:id="@+id/iv_head"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_centerInParent="true"
android:scaleType="centerCrop"
android:src="@drawable/audioim_head_place_hold_pic" />
</RelativeLayout>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/rl_head"
android:layout_centerHorizontal="true"
android:layout_marginTop="-20dp"
android:textColor="@color/white"
android:textSize="26sp"
tools:text="用户" />
<!--自定义弹窗-->
<TextView
android:id="@+id/tv_toast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_name"
android:layout_centerHorizontal="true"
android:layout_marginTop="34dp"
android:background="@drawable/audioim_toast_view_background"
android:elevation="6dp"
android:paddingLeft="11dp"
android:paddingTop="4dp"
android:paddingRight="11dp"
android:paddingBottom="4dp"
android:textColor="@color/white"
android:visibility="gone"
tools:text="连接中..."
tools:visibility="visible" />
<!-- 倾诉剩余时间-->
<RelativeLayout
android:id="@+id/rl_remain_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/rl_hang_up"
android:layout_marginBottom="20dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/tv_notes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="本次倾诉时间还有"
android:textColor="@color/platform_color_30FFFFFF"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_remain_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_notes"
android:layout_centerHorizontal="true"
android:text="23:23"
android:textColor="@color/white"
android:textSize="20sp" />
</RelativeLayout>
<!--挂断按钮-->
<RelativeLayout
android:id="@+id/rl_hang_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="72dp"
android:layout_marginBottom="18dp"
android:onClick="hangUpClick">
<ImageView
android:id="@+id/iv_hang_up"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_centerHorizontal="true"
android:src="@drawable/audioim_img_hang_up"
android:visibility="visible" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_hang_up"
android:layout_centerHorizontal="true"
android:layout_marginTop="9dp"
android:text="挂断"
android:textColor="@color/white"
android:textSize="12sp" />
</RelativeLayout>
<!-- 接听按钮-->
<RelativeLayout
android:id="@+id/rl_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="72dp"
android:layout_marginBottom="18dp"
android:onClick="onCall">
<ImageView
android:id="@+id/iv_hands_call"
android:layout_width="66dp"
android:layout_height="66dp"
android:src="@drawable/audioim_audio_home_img_answer" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_hands_call"
android:layout_centerHorizontal="true"
android:layout_marginTop="9dp"
android:text="接听"
android:textColor="@color/white"
android:textSize="12sp" />
</RelativeLayout>
<!-- 免提按钮-->
<RelativeLayout
android:id="@+id/rl_hands_free"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="72dp"
android:layout_marginBottom="18dp"
android:onClick="onSwitchSpeakerphoneClicked"
android:visibility="gone">
<ImageView
android:id="@+id/iv_hands_free"
android:layout_width="66dp"
android:layout_height="66dp"
android:src="@drawable/audioim_img_hands_free_unuse" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_hands_free"
android:layout_centerHorizontal="true"
android:layout_marginTop="9dp"
android:text="免提"
android:textColor="@color/white"
android:textSize="12sp" />
</RelativeLayout>
</RelativeLayout>
\ 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