与 IM 业务集成
您可以将 CallPlus 集成到基于 IMLib SDK 或 IMKit 的应用程序中,为用户提供使用通话和聊天服务的无缝体验。
在会话页面插入通话结束消息
应用程序可以通话结束时返回聊天视图,并在会话页面中展示通话结束的消息。
为便于与 IMLib SDK 和 IMKit 集成,从 2.1.1 版本开始,CallPlus SDK 可在通话结束时返回一个 IM 消息对象(RCMessage
),其中包含消息内容对象 RCCallPlusSummaryMessage。应用程序可以通过调用 IMLib SDK 或 IMKit 的接口直接将该消息 内容对象插入到会话中。如果使用 IMKit,请按照本文档创建一个专用的消息展示模板。
获取通话结束消息
重要
CallPlus SDK 暂仅支持在一对一通话(通话类型为
RCCallPlusSingleType
)结束时返回用于展示通话记录的消息内容。暂不支持支持多人通话。
通话正常、异常结束,或在未接听时挂断,CallPlus SDK 会触发 RCCallPlusEventDelegate 的 didReceivedCallPlusSummaryMessage: 回调方法,可在该回调方法获取到通话结束的消息内容对象。
如果被叫时用户不在线,CallPlus SDK 会在下次成功连接后立即获取离线时产生的 RCCallPlusSummaryMessage
。为了确保 CallPlus SDK 可在连接成功后立即获取到这些消息,请在连接之前调用 setCallEventDelegate: 注册监听器。
[[RCCallPlusClient sharedInstance] setCallEventDelegate:self];
在 IMKit 会话页面中插入通话结束消息
以下步骤描述了如何在集成 IMKit 的项目中为 RCCallPlusSummaryMessage
的创建消息展示模板,以及如何将消息插入到本地会话页面中。
-
自定义一个展示通话摘要的 Cell 类,比如使用
MyCallDetailMessageCell
。您也可以参考 IMKit 文档修改消息的展示样式。-
MyCallDetailMessageCell.h
//
// RCCallDetailMessageCell.h
// RongCallKit
//
// Created by RongCloud on 16/3/20.
// Copyright © 2016年 RongCloud. All rights reserved.
//
#if __has_include(<RongIMKit/RongIMKit.h>)
#import <RongIMKit/RongIMKit.h>
#else
#import "RongIMKit.h"
#endif
/*!
通话摘要的消息Cell
*/
@interface MyCallDetailMessageCell : RCMessageCell
/*!
消息文本的Label
*/
@property (strong, nonatomic) UILabel *textLabel;
/*!
标识媒体类型的Icon
*/
@property (strong, nonatomic) UIImageView *mediaTypeIcon;
/*!
设置当前消息Cell的数据模型
@param model 消息Cell的数据模型
*/
- (void)setDataModel:(RCMessageModel *)model;
@end -
MyCallDetailMessageCell.m
//
// RCCallDetailMessageCell.m
// RongCallKit
//
// Created by RongCloud on 16/3/20.
// Copyright © 2016年 RongCloud. All rights reserved.
//
#import "MyCallDetailMessageCell.h"
#import "MyCallSummaryMessage.h"
@implementation MyCallDetailMessageCell
+ (CGSize)sizeForMessageModel:(RCMessageModel *)model
withCollectionViewWidth:(CGFloat)collectionViewWidth
referenceExtraHeight:(CGFloat)extraHeight {
//当为单聊时,需要显示头像,且内容区域高度小于头像高度,所以内容区域高度就是头像高度,然后加上上下10padding。
CGFloat height = RCKitConfigCenter.ui.globalMessagePortraitSize.height;
height += extraHeight;
return CGSizeMake(collectionViewWidth, height);
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialize];
}
return self;
}
- (void)initialize {
[self showBubbleBackgroundView:YES];
self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.textLabel setFont:[[RCKitConfig defaultConfig].font fontOfSecondLevel]];
self.textLabel.numberOfLines = 0;
[self.textLabel setLineBreakMode:NSLineBreakByWordWrapping];
[self.textLabel setTextAlignment:NSTextAlignmentLeft];
if (RC_IOS_SYSTEM_VERSION_LESS_THAN(@"7.0")) {
[self.textLabel setBackgroundColor:[UIColor clearColor]];
}
[self.messageContentView addSubview:self.textLabel];
self.mediaTypeIcon = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.messageContentView addSubview:self.mediaTypeIcon];
}
- (void)setDataModel:(RCMessageModel *)model {
[super setDataModel:model];
[self setAutoLayout];
}
- (void)setAutoLayout {
MyCallSummaryMessage *callMessage = (MyCallSummaryMessage *)self.model.content;
if (callMessage.duration > 0) {
self.textLabel.text =
[NSString stringWithFormat:@"通话时长 %@",
[self getReadableStringForTime:(long)callMessage.duration]];
} else {
self.textLabel.text = [self getReadableStringForMessageCell:callMessage.hangupReason];
}
if (callMessage.mediaType == MyCallMediaVideo && self.messageDirection == MessageDirection_RECEIVE) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"video_left.png"];
} else if (callMessage.mediaType == MyCallMediaVideo && self.messageDirection == MessageDirection_SEND) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"video_right.png"];
} else if (callMessage.mediaType == MyCallMediaAudio && self.messageDirection == MessageDirection_RECEIVE &&
callMessage.duration > 0) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"audio_receiver_left.png"];
} else if (callMessage.mediaType == MyCallMediaAudio && self.messageDirection == MessageDirection_RECEIVE &&
callMessage.duration <= 0) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"audio_receiver_up_left.png"];
} else if (callMessage.mediaType == MyCallMediaAudio && self.messageDirection == MessageDirection_SEND &&
callMessage.duration > 0) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"audio_receiver_right.png"];
} else if (callMessage.mediaType == MyCallMediaAudio && self.messageDirection == MessageDirection_SEND &&
callMessage.duration <= 0) {
self.mediaTypeIcon.image = [UIImage imageNamed:@"audio_receiver_up_right.png"];
}
CGSize __textSize = [RCKitUtility
getTextDrawingSize:self.textLabel.text
font:self.textLabel.font
constrainedSize:CGSizeMake(self.baseContentView.bounds.size.width -
(10 + RCKitConfigCenter.ui.globalMessagePortraitSize.width + 10) * 2 -
(14 + 20 + 7 + 10),
8000)];
__textSize = CGSizeMake(ceilf(__textSize.width), ceilf(__textSize.height));
float maxWidth = self.baseContentView.bounds.size.width -
(10 + RCKitConfigCenter.ui.globalMessagePortraitSize.width + 10) * 2 - 5 - (14 + 20 + 7 + 10);
if (__textSize.width > maxWidth) {
__textSize.width = maxWidth;
}
CGSize __labelSize = CGSizeMake(__textSize.width, __textSize.height + 5);
CGFloat __bubbleWidth = __labelSize.width + (14 + 20 + 7 + 10) < 50 ? 50 : (__labelSize.width + (14 + 20 + 7 + 10));
CGFloat __bubbleHeight = __labelSize.height + 5 + 5 < RCKitConfigCenter.ui.globalMessagePortraitSize.height ? RCKitConfigCenter.ui.globalMessagePortraitSize.height : (__labelSize.height + 5 + 5);
CGSize __bubbleSize = CGSizeMake(__bubbleWidth, __bubbleHeight);
self.messageContentView.contentSize = __bubbleSize;
if (self.model.messageDirection == MessageDirection_SEND) {
self.textLabel.frame =
CGRectMake(10, (__bubbleHeight - __labelSize.height) / 2, __labelSize.width, __labelSize.height);
self.mediaTypeIcon.frame = CGRectMake(CGRectGetMaxX(self.textLabel.frame) + 7, (__bubbleHeight - 20) / 2, 20, 20);
[self.textLabel setTextColor:RCDYCOLOR(0x262626, 0x040A0F)];
} else {
self.mediaTypeIcon.frame = CGRectMake(10, (__bubbleHeight - 20) / 2, 20, 20);
self.textLabel.frame =
CGRectMake(CGRectGetMaxX(self.mediaTypeIcon.frame) + 7, (__bubbleHeight - __labelSize.height) / 2,
__labelSize.width, __labelSize.height);
[self.textLabel setTextColor:[RCKitUtility generateDynamicColor:HEXCOLOR(0x262626)
darkColor:RCMASKCOLOR(0xffffff, 0.8)]];
}
}
// 显示时间
- (NSString *)getReadableStringForTime:(long)sec {
if (sec < 60 * 60) {
return [NSString stringWithFormat:@"%02ld:%02ld", sec / 60, sec % 60];
} else {
return [NSString stringWithFormat:@"%02ld:%02ld:%02ld", sec / 60 / 60, (sec / 60) % 60, sec % 60];
}
}
// 处理原因
- (NSString *)getReadableStringForMessageCell:(MyCallDisconnectReason)hangupReason {
NSString *reason;
switch (hangupReason) {
case 1001:case 1002:
reason = @"通话未接听";
break;
case 1003:
reason = @"通话取消";
break;
case 1004:
reason = @"通话拒绝";
break;
default:
reason = @"通话异常";
break;
}
return reason;
}
@end
-
-
在会话页面重写对应的方法绑定消息类与展示的 Cell 类:
// 注册自定义消息和cell
- (void)registerCustomCellsAndMessages {
[super registerCustomCellsAndMessages];
// 注册自定义测试消息 Cell
[self registerClass:[MyCallDetailMessageCell class] forMessageClass:[RCCallPlusSummaryMessage class]];
} -
对收到的通话消息进行插入数据库操作,如果用户此时正在会话页面,还需要在该会话页面的 UI 上插入这个消息保证消息的及时刷新:
- (void)didReceivedCallPlusSummaryMessage:(RCMessage *)callSummaryMessage {
RCCallPlusSummaryMessage *summaryMessageContent = (RCCallPlusSummaryMessage *)callSummaryMessage.content;
RCMessage *message = nil;
if (callSummaryMessage.messageDirection == 1) {
/// 呼出通话
[[RCCoreClient sharedCoreClient] insertOutgoingMessage:ConversationType_PRIVATE
targetId:callSummaryMessage.targetId
sentStatus:SentStatus_SENT
content:summaryMessageContent
sentTime:callSummaryMessage.sentTime completion:^(RCMessage * _Nullable message) {
if (![message.targetId isEqualToString:self.chatVC.targetId]) return;
dispatch_async(dispatch_get_main_queue(), ^{
[self.chatVC appendAndDisplayMessage:message];
});
}];
}
else {
RCReceivedStatusInfo *statusInfo = [[RCReceivedStatusInfo alloc] initWithReceivedStatus:0];
[statusInfo markAsRead];
[[RCCoreClient sharedCoreClient] insertIncomingMessage:ConversationType_PRIVATE
targetId:callSummaryMessage.targetId
senderUserId:summaryMessageContent.callerUserId
receivedStatusInfo:statusInfo
content:summaryMessageContent
sentTime:callSummaryMessage.sentTime completion:^(RCMessage * _Nullable message) {
if (![message.targetId isEqualToString:self.chatVC.targetId]) return;
dispatch_async(dispatch_get_main_queue(), ^{
[self.chatVC appendAndDisplayMessage:message];
});
}];
}
}
在 IMLib 本地会话中插入通话结束消息
如果应用程序仅集成 IMLib SDK,您需要将 RCCallPlusSummaryMessage
插入到本地会话中,并自行刷新 UI。
- (void)didReceivedCallPlusSummaryMessage:(RCMessage *)callSummaryMessage {
RCCallPlusSummaryMessage *summaryMessageContent = (RCCallPlusSummaryMessage *)callSummaryMessage.content;
RCMessage *message = nil;
if (callSummaryMessage.messageDirection == 1) {
/// 呼出通话
[[RCCoreClient sharedCoreClient] insertOutgoingMessage:ConversationType_PRIVATE
targetId:callSummaryMessage.targetId
sentStatus:SentStatus_SENT
content:summaryMessageContent
sentTime:callSummaryMessage.sentTime completion:^(RCMessage * _Nullable message) {
}];
}
else {
RCReceivedStatusInfo *statusInfo = [[RCReceivedStatusInfo alloc] initWithReceivedStatus:0];
[statusInfo markAsRead];
[[RCCoreClient sharedCoreClient] insertIncomingMessage:ConversationType_PRIVATE
targetId:callSummaryMessage.targetId
senderUserId:summaryMessageContent.callerUserId
receivedStatusInfo:statusInfo
content:summaryMessageContent
sentTime:callSummaryMessage.sentTime completion:^(RCMessage * _Nullable message) {
}];
}
}