与 IM 业务集成
您可以将 CallPlus 集成到基于 IMLib SDK 或 IMKit 的应用程序中,为用户提供通话和聊天服务的无缝体验。
在会话页面插入通话结束消息
应用程序可以在通话结束时返回聊天视图,并在会话页面中展示通话结束的消息。
为便于与 IMLib SDK 和 IMKit 集成,从 2.1.1 版本开始,CallPlus SDK 可在通话结束时返回一个 IM 消息对象(RCMessage),其中包含消息内容对象 RCCallPlusSummaryMessage。应用程序可以通过调用 IMLib 或 IMKit SDK 的插入消息接口,直接将该消息内容对象插入到会话中。如果使用 IMKit,请按照本文档创建一个专用的消息展示模板。
CallPlus SDK 暂仅支持在一对一通话(通话类型为 RCCallPlusSingleType)结束时返回用于展示通话记录的消息内容。暂不支持多人通话。
获取通话结束消息
通话正常、异常结束,或在未接听时挂断,CallPlus SDK 会触发 RCCallPlusEventDelegate 的 didReceivedCallPlusSummaryMessage: 回调方法,您可以在该回调方法中获取到通话结束的消息内容对象。
如果被叫时用户不在线,CallPlus SDK 会在下次成功连接后立即获取离线时产生的 RCCallPlusSummaryMessage。为了确保 CallPlus SDK 可在连接成功后立即获取到这些消息,请在连接 IM 之前调用 setCallEventDelegate: 注册监听器。
[[RCCallPlusClient sharedInstance] setCallEventDelegate:self];
在 IMKit 会话页面中插入通话结束消息
以下步骤描述了如何在集成 IMKit 的项目中为 RCCallPlusSummaryMessage 创建消息展示模板,以及如何将消息插入到本地会话页面中。
- 
自定义一个展示通话摘要的 Cell 类,比如使用 MyCallDetailMessageCell。您也可以参考 IMKit 文档修改消息的展示样式。- 
MyCallDetailMessageCell.hObjective C//
 // 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 的数据模型
 */
 - (void)setDataModel:(RCMessageModel *)model;
 @end
- 
MyCallDetailMessageCell.mObjective C//
 // 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 {
 // 当为单聊时,需要显示头像,且内容区域高度小于头像高度,所以内容区域高度就是头像高度,然后加上上下 10 padding
 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 类: Objective C// 注册自定义消息和cell
 - (void)registerCustomCellsAndMessages {
 [super registerCustomCellsAndMessages];
 // 注册自定义测试消息 Cell
 [self registerClass:[MyCallDetailMessageCell class] forMessageClass:[RCCallPlusSummaryMessage class]];
 }
- 
对收到的通话消息进行插入数据库操作,如果用户此时正在会话页面,还需要在该 IMKit 会话页面的 UI 上调用 RCConversationViewController 的 appendAndDisplayMessage:方法插入这个消息,保证消息的及时刷新:Objective C- (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) {
        }];
    }
}