跳到主要内容

与 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 会触发 RCCallPlusEventDelegatedidReceivedCallPlusSummaryMessage: 回调方法,可在该回调方法获取到通话结束的消息内容对象。

如果被叫时用户不在线,CallPlus SDK 会在下次成功连接后立即获取离线时产生的 RCCallPlusSummaryMessage。为了确保 CallPlus SDK 可在连接成功后立即获取到这些消息,请在连接之前调用 setCallEventDelegate: 注册监听器。

[[RCCallPlusClient sharedInstance] setCallEventDelegate:self];

在 IMKit 会话页面中插入通话结束消息

以下步骤描述了如何在集成 IMKit 的项目中为 RCCallPlusSummaryMessage 的创建消息展示模板,以及如何将消息插入到本地会话页面中。

  1. 自定义一个展示通话摘要的 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

  2. 在会话页面重写对应的方法绑定消息类与展示的 Cell 类:

    // 注册自定义消息和cell
    - (void)registerCustomCellsAndMessages {
    [super registerCustomCellsAndMessages];
    // 注册自定义测试消息 Cell
    [self registerClass:[MyCallDetailMessageCell class] forMessageClass:[RCCallPlusSummaryMessage class]];
    }
  3. 对收到的通话消息进行插入数据库操作,如果用户此时正在会话页面,还需要在该会话页面的 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) {

}];
}
}