与 IM 业务集成
您可以将 CallPlus 集成到基于 IMLib SDK 或 IMKit 的应用程序中,为用户提供使用通话和聊天服务的无缝体验。
在会话页面插入通话结束消息
应用程序可以通话结束时返回聊天视图,并在会话页面中展示通话结束的消息。
为便于与 IMLib SDK 和 IMKit 集成,从 2.1.1 版本开始 ,CallPlus SDK 可在通话结束时返回一个 IM 消息对象(RCMessage
),其中包含消息内容对象 RCCallPlusSummaryMessageContent。应用程序可以通过调用 IMLib SDK 或 IMKit 的接口直接将该消息内容对象插入到会话中。如果使用 IMKit,请按照本文档创建一个专用的消息展示模板。
获取通话结束消息
重要
CallPlus SDK 暂仅支持在一对一通话(通话类型为
RCCallPlusType.PRIVATE
)结束时返回用于展示通话记录的消息内容。暂不支持支持多人通话。
通话正常、异常结束,或在未接听时挂断,CallPlus SDK 会触发 IRCCallPlusEventListener 的 onReceivedCallPlusSummaryMessage 回调方法,可在该回调方法获取到通话结束的消息内容对象。
如果被叫时用户不在线,CallPlus SDK 会在下次成功连接后立即获取离线时产生的 RCCallPlusSummaryMessageContent
。为了确保 CallPlus SDK 可在连接成功后立即获取到这 些消息,请在连接之前调用 setCallPlusEventListener 注册监听器。
RCCallPlusClient.getInstance().setCallPlusEventListener(new IRCCallPlusEventListener() {
@Override
public void onReceivedCall(RCCallPlusSession callSession, String extra) {
}
/**
* 己方参与过的通话结束时,通话消息监听回调
*
* <p>目前,本回调仅在单人通话结束时(通话类型为{@link RCCallPlusType#PRIVATE})触发,并返回相应的信息。
*
* <p>在 IM Lib 登录成功后,将获取离线时产生的通话消息({@link
* cn.rongcloud.callplus.api.RCCallPlusSummaryMessageContent })。为了确保在登录成功后立即获取到这些消息,请在IM
* Lib初始化之前注册监听器:{@link RCCallPlusClient#setCallPlusEventListener(IRCCallPlusEventListener)}
*
* @param message {@link cn.rongcloud.callplus.api.RCCallPlusSummaryMessageContent }
*/
@Override
public void onReceivedCallPlusSummaryMessage(Message message) {
IRCCallPlusEventListener.super.onReceivedCallPlusSummaryMessage(message);
insertIntoDatabase(message);
}
});
在 IMKit 会话页面中插入通话结束消息
以下步骤描述了如何在 IMKit 中为 RCCallPlusSummaryMessageContent
的创建消息展示模板,以及如何将消息插入到本地会话页面中。
-
创建通话结束消息展示模版类,所有消息展示模板都继承自
BaseMessageItemProvider<CustomMessage>
。需要修改消息展示样式,请参考修改消息的展示样式。import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import cn.rongcloud.callplus.api.RCCallPlusMediaType;
import cn.rongcloud.callplus.api.RCCallPlusReason;
import cn.rongcloud.callplus.api.RCCallPlusSummaryMessageContent;
import io.rong.imkit.conversation.messgelist.provider.BaseMessageItemProvider;
import io.rong.imkit.model.UiMessage;
import io.rong.imkit.widget.adapter.IViewProviderListener;
import io.rong.imkit.widget.adapter.ViewHolder;
import io.rong.imlib.RongCoreClient;
import io.rong.imlib.model.MessageContent;
import java.util.List;
public class MyCallPlusRecordMessageItemProvider extends BaseMessageItemProvider<RCCallPlusSummaryMessageContent> {
@Override
protected ViewHolder onCreateMessageContentViewHolder(ViewGroup parent, int viewType) {
//todo R.layout.rc_text_message_item 布局文件来自于IM Kit SDK中
View textView =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.rc_text_message_item, parent, false);
return new ViewHolder(parent.getContext(), textView);
}
@Override
protected void bindMessageContentViewHolder(ViewHolder holder, ViewHolder parentHolder, RCCallPlusSummaryMessageContent rcCallPlusSummaryMessageContent, UiMessage uiMessage, int position, List<UiMessage> list, IViewProviderListener<UiMessage> listener) {
final TextView view = holder.getView(io.rong.imkit.R.id.rc_text);
//todo R.drawable.rc_ic_bubble_right 和 R.drawable.rc_ic_bubble_left 文件来自于IM Kit SDK中
if (TextUtils.equals(rcCallPlusSummaryMessageContent.getCallerUserId(), RongCoreClient.getInstance().getCurrentUserId())) {
view.setBackgroundResource(R.drawable.rc_ic_bubble_right);
} else {
view.setBackgroundResource(R.drawable.rc_ic_bubble_left);
}
view.setText(getCallReasonString(rcCallPlusSummaryMessageContent));
view.setCompoundDrawablePadding(15);
}
@Override
protected boolean onItemClick(ViewHolder holder, RCCallPlusSummaryMessageContent rcCallPlusSummaryMessageContent, UiMessage uiMessage, int position, List<UiMessage> list, IViewProviderListener<UiMessage> listener) {
Context context = holder.getContext();
Toast.makeText(
context,
"点击了CallPlus自定义通话消息",
Toast.LENGTH_SHORT)
.show();
return false;
}
@Override
protected boolean isMessageViewType(MessageContent messageContent) {
return messageContent instanceof RCCallPlusSummaryMessageContent;
}
@Override
public Spannable getSummarySpannable(Context context, RCCallPlusSummaryMessageContent rcCallPlusSummaryMessageContent) {
if (rcCallPlusSummaryMessageContent.getMediaType() == RCCallPlusMediaType.AUDIO) {
return new SpannableString("语音通话");
} else {
return new SpannableString("视频通话");
}
}
//todo 该实例代码转换挂断原因仅供参考,请按照APP产品定义修改该逻辑
private String getCallReasonString(RCCallPlusSummaryMessageContent messageContent) {
RCCallPlusReason reason = messageContent.getEndCallReason();
String str = reason.name();
switch (reason) {
case NONE:
case IDLE:
case ENDED:
case MISSED:
long duration = messageContent.getEndTime() - messageContent.getCallStartTime();
str = "通话结束,时长"+ (duration/1000) +" 秒";
break;
case BUSY_LINE_RINGING:
case BUSY_LINE_WAIT:
str = "忙线中";
break;
case CONNECTING:
case ON_CALL:
case ON_PHONE:
case ON_PHONE_ENDED:
case ON_DEVICE_DISABLE:
case ON_DEVICE_ENABLE:
case CALLING:
case INVITED:
case RINGING:
//非挂断原因 不需要转换
break;
case BOUNDARY_ENDED:
str = "异常结束";
break;
case NO_ANSWER:
str = "对方未接听";
break;
case CANCELED:
str = "已取消";
break;
case DECLINED:
str = "已拒绝";
break;
case OTHER_CLIENT_CALLING:
str = "己方其他端正在响铃中";
break;
case ACCEPT_BY_OTHER_CLIENT:
str = "己方其他端已接听通话";
break;
case JOIN_RTC_ERROR:
case PUBLISH_ERROR:
case SUBSCRIBE_ERROR:
str = "己方网络异常";
break;
case KICKED_BY_SERVER:
str = "己方已被禁止通话";
break;
case CONNECTION_ERROR:
str = "己方连接断开";
break;
case LOGOUT:
str = "己方退出登录";
break;
case OTHER_CLIENT_LOGIN:
str = "被其他端登录IM挤下线";
break;
case OTHER_JOINED_RTC:
str = "其他端已加入新通话";
break;
case OTHER_CLIENT_IN_RTC:
str = "其他端已在其他通话中";
break;
case REMOTE_JOIN_RTC_ERROR:
case REMOTE_PUBLISH_ERROR:
case REMOTE_SUBSCRIBE_ERROR:
str = "对方网络异常";
break;
case REMOTE_KICKED_BY_SERVER:
str = "对方已被禁止通话";
break;
case REMOTE_CONNECTION_ERROR:
str = "对方连接断开";
break;
case REMOTE_LOGOUT:
str = "对方退出登录";
break;
case REMOTE_OTHER_CLIENT_LOGIN:
str = "对方被其他端登录挤下线";
break;
case REMOTE_OTHER_JOINED_RTC:
str = "对方其他端已加入新通话";
break;
case REMOTE_OTHER_CLIENT_IN_RTC:
str = "对方其他端已在其他通话中";
break;
case HANGUP_BY_OTHER_CLIENT:
str = "己方其他端已挂断通话";
break;
}
return str;
}
} -
在 IMKit 初始化后,注册自定义的消息展示模板。
RongConfigCenter.conversationConfig().addMessageProvider(new MyCallPlusRecordMessageItemProvider());
-
在
IRCCallPlusEventListener
的 onReceivedCallPlusSummaryMessage 回调方法中获取Message
对象中包含的 RCCallPlusSummaryMessageContent,在本地会话页面中插入一条通话结束消息。@Override
public void onReceivedCallPlusSummaryMessage(Message message) {
IRCCallPlusEventListener.super.onReceivedCallPlusSummaryMessage(message);
insertIntoDatabase(message);
}以下代码示例实现了一个
insertIntoDatabase
方法,其中使用 IMKit 的核心类IMCenter
的插入消息方法,IMKit 会负责刷新会话页面。private void insertIntoDatabase(Message message) {
RCCallPlusSummaryMessageContent messageContent = (RCCallPlusSummaryMessageContent) message.getContent();
//获取目标 id。根据不同的 conversationType,可能是对端用户 id、群组 id。
String targetId = messageContent.getTargetId();
//通话发起人 ID
String callerUserId = messageContent.getCallerUserId();
long sentTime = message.getSentTime();
//如果通话发起人是本人,则消息的插入方向为发送
if (TextUtils.equals(callerUserId, RongCoreClient.getInstance().getCurrentUserId())) {
Message.SentStatus sentStatus = io.rong.imlib.model.Message.SentStatus.SENT;
/**
* 向本地会话中插入一条消息,方向为发送。
*
* <p>这条消息只是插入本地会话,不会实际发送给服务器和对方。 插入消息需为入库消息,即 {@link MessageTag#ISPERSISTED},否者会回调 {@link
* IRongCoreEnum.CoreErrorCode#RC_INVALID_PARAMETER_MSG_TAG}
*
* @param type 会话类型。
* @param targetId 会话 id。比如私人会话时,是对方的 id; 群组会话时,是群 id; 讨论组会话时,则为该讨论组的 id。
* @param sentStatus 发送状态 {@link Message.SentStatus}
* @param content 消息内容。如{@link TextMessage} {@link ImageMessage} 等。
* @param sentTime 消息的发送时间 {@link Message#getSentTime()} 。
* @param resultCallback 获得消息发送实体的回调。
* @since 5.0.0
*/
IMCenter.getInstance()
.insertOutgoingMessage(ConversationType.PRIVATE, targetId, sentStatus, messageContent, sentTime, new RongIMClient.ResultCallback<Message>() {
@Override
public void onSuccess(Message message) {
}
@Override
public void onError(RongIMClient.ErrorCode e) {
}
});
} else {
Message.ReceivedStatus receivedStatus = new ReceivedStatus(1); // 默认插入的通话记录为已读
/**
* 向本地会话中插入一条消息,方向为接收。
*
* <p>这条消息只是插入本地会话,不会实际发送给服务器和对方。插入消息需为入库消息,即 {@link MessageTag#ISPERSISTED},否者会回调 {@link
* IRongCoreEnum.CoreErrorCode#RC_INVALID_PARAMETER_MSG_TAG}。
*
* @param type 会话类型。
* @param targetId 会话 id。比如私人会话时,是对方的 id; 群组会话时,是群 id; 讨论组会话时,则为该讨论组的 id。
* @param senderUserId 发送方 id
* @param receivedStatus 接收状态 {@link Message.ReceivedStatus}
* @param content 消息内容。如 {@link TextMessage} {@link ImageMessage}等。
* @param sentTime 消息的发送时间 {@link Message#getSentTime()} 。
* @param resultCallback 获得消息发送实体的回调。
* @since 5.0.0
*/
IMCenter.getInstance().insertIncomingMessage(ConversationType.PRIVATE, targetId, callerUserId, receivedStatus, messageContent, sentTime, new RongIMClient.ResultCallback<Message>() {
@Override
public void onSuccess(Message message) {
}
@Override
public void onError(RongIMClient.ErrorCode e) {
}
});
}
}