Files
featherVoice/TUIKit/TUIChat/BaseDataProvider/Base/TUIChatCallingDataProvider.m

552 lines
22 KiB
Mathematica
Raw Normal View History

2025-08-08 10:49:36 +08:00
//
// TUIChatCallingDataProvider.m
// TUIChat
//
// Created by harvy on 2022/12/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIChatCallingDataProvider.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUICore.h>
#import <TUICore/TUILogin.h>
#import "TUIMessageBaseDataProvider.h"
typedef NSString *TUIChatMessageID;
typedef NSDictionary *TUIChatCallingJsonData;
// ********************************************************************
// TUIChatCallingInfo
// ********************************************************************
@interface TUIChatCallingInfo : NSObject <TUIChatCallingInfoProtocol>
@property(nonatomic, strong) TUIChatMessageID msgID;
@property(nonatomic, strong, nullable) TUIChatCallingJsonData jsonData;
@property(nonatomic, strong, nullable) V2TIMSignalingInfo *signalingInfo;
@property(nonatomic, strong, nullable) V2TIMMessage *innerMessage;
@property(nonatomic, assign) TUIChatCallingMessageAppearance style;
@end
@implementation TUIChatCallingInfo
#pragma mark - TUIChatCallingInfoProtocol
- (TUICallProtocolType)protocolType {
if (self.jsonData == nil || self.signalingInfo == nil || self.innerMessage == nil) {
return TUICallProtocolTypeUnknown;
}
TUICallProtocolType type = TUICallProtocolTypeUnknown;
switch (self.signalingInfo.actionType) {
case SignalingActionType_Invite: {
NSDictionary *data = [self.jsonData objectForKey:@"data"];
if (data && [data isKindOfClass:NSDictionary.class]) {
// New version for calling
NSString *cmd = [data objectForKey:@"cmd"];
if ([cmd isKindOfClass:NSString.class]) {
if ([cmd isEqualToString:@"switchToAudio"]) {
type = TUICallProtocolTypeSwitchToAudio;
} else if ([cmd isEqualToString:@"hangup"]) {
type = TUICallProtocolTypeHangup;
} else if ([cmd isEqualToString:@"videoCall"]) {
type = TUICallProtocolTypeSend;
} else if ([cmd isEqualToString:@"audioCall"]) {
type = TUICallProtocolTypeSend;
} else {
type = TUICallProtocolTypeUnknown;
}
} else {
NSLog(@"calling protocol error, %@", self.jsonData);
type = TUICallProtocolTypeUnknown;
}
} else {
// Compatiable
NSNumber *callEnd = [self.jsonData objectForKey:@"call_end"];
if (callEnd && [callEnd isKindOfClass:NSNumber.class]) {
type = TUICallProtocolTypeHangup;
} else {
type = TUICallProtocolTypeSend;
}
}
} break;
case SignalingActionType_Cancel_Invite: {
type = TUICallProtocolTypeCancel;
} break;
case SignalingActionType_Accept_Invite: {
NSDictionary *data = [self.jsonData objectForKey:@"data"];
if (data && [data isKindOfClass:NSDictionary.class]) {
// New version for calling
NSString *cmd = [data objectForKey:@"cmd"];
if ([cmd isKindOfClass:NSString.class]) {
if ([cmd isEqualToString:@"switchToAudio"]) {
type = TUICallProtocolTypeSwitchToAudioConfirm;
} else {
type = TUICallProtocolTypeAccept;
}
} else {
NSLog(@"calling protocol error, %@", self.jsonData);
type = TUICallProtocolTypeAccept;
}
} else {
// Compatiable
type = TUICallProtocolTypeAccept;
}
} break;
case SignalingActionType_Reject_Invite: {
if ([self.jsonData objectForKey:@"line_busy"]) {
type = TUICallProtocolTypeLineBusy;
} else {
type = TUICallProtocolTypeReject;
}
} break;
case SignalingActionType_Invite_Timeout: {
type = TUICallProtocolTypeTimeout;
} break;
default:
type = TUICallProtocolTypeUnknown;
break;
}
return type;
}
- (TUICallStreamMediaType)streamMediaType {
TUICallProtocolType protocolType = self.protocolType;
if (protocolType == TUICallProtocolTypeUnknown) {
return TUICallStreamMediaTypeUnknown;
}
// Default type
TUICallStreamMediaType type = TUICallStreamMediaTypeUnknown;
NSNumber *callType = [self.jsonData objectForKey:@"call_type"];
if (callType && [callType isKindOfClass:NSNumber.class]) {
if ([callType integerValue] == 1) {
type = TUICallStreamMediaTypeVoice;
} else if ([callType integerValue] == 2) {
type = TUICallStreamMediaTypeVideo;
}
}
// Read from special protocol
if (protocolType == TUICallProtocolTypeSend) {
NSDictionary *data = [self.jsonData objectForKey:@"data"];
if (data && [data isKindOfClass:NSDictionary.class]) {
NSString *cmd = [data objectForKey:@"cmd"];
if ([cmd isEqual:@"audioCall"]) {
type = TUICallStreamMediaTypeVoice;
} else if ([cmd isEqual:@"videoCall"]) {
type = TUICallStreamMediaTypeVideo;
}
}
} else if (protocolType == TUICallProtocolTypeSwitchToAudio || protocolType == TUICallProtocolTypeSwitchToAudioConfirm) {
type = TUICallStreamMediaTypeVideo;
}
return type;
}
- (TUICallParticipantType)participantType {
if (self.protocolType == TUICallProtocolTypeUnknown) {
return TUICallParticipantTypeUnknown;
}
if (self.signalingInfo.groupID.length > 0) {
return TUICallParticipantTypeGroup;
} else {
return TUICallParticipantTypeC2C;
}
}
- (NSString *)caller {
NSString *callerID = nil;
NSDictionary *data = [self.jsonData objectForKey:@"data"];
if (data && [data isKindOfClass:NSDictionary.class]) {
NSString *inviter = [data objectForKey:@"inviter"];
if (inviter && [inviter isKindOfClass:NSString.class]) {
callerID = inviter;
}
}
if (callerID == nil) {
callerID = TUILogin.getUserID;
}
return callerID;
}
- (TUICallParticipantRole)participantRole {
if ([self.caller isEqualToString:TUILogin.getUserID]) {
return TUICallParticipantRoleCaller;
} else {
return TUICallParticipantRoleCallee;
}
}
- (BOOL)excludeFromHistory {
if (self.style == TUIChatCallingMessageAppearanceSimplify) {
return self.protocolType != TUICallProtocolTypeUnknown && self.innerMessage.isExcludedFromLastMessage && self.innerMessage.isExcludedFromUnreadCount;
} else {
return NO;
}
}
- (NSString *)content {
if (self.style == TUIChatCallingMessageAppearanceSimplify) {
return [self contentForSimplifyAppearance];
} else {
return [self contentForDetailsAppearance];
}
}
- (TUICallMessageDirection)direction {
if (self.style == TUIChatCallingMessageAppearanceSimplify) {
return [self directionForSimplifyAppearance];
} else {
return [self directionForDetailsAppearance];
}
}
- (BOOL)showUnreadPoint {
if (self.excludeFromHistory) {
return NO;
}
return (self.innerMessage.localCustomInt == 0) && (self.participantRole == TUICallParticipantRoleCallee) &&
(self.participantType == TUICallParticipantTypeC2C) &&
(self.protocolType == TUICallProtocolTypeCancel || self.protocolType == TUICallProtocolTypeTimeout ||
self.protocolType == TUICallProtocolTypeLineBusy);
}
- (BOOL)isUseReceiverAvatar {
if (self.style == TUIChatCallingMessageAppearanceSimplify) {
return [self isUseReceiverAvatarForSimplifyAppearance];
} else {
return [self isUseReceiverAvatarForDetailsAppearance];
}
}
- (NSArray<NSString *> *)participantIDList {
NSMutableArray *arrayM = [NSMutableArray array];
if (self.signalingInfo.inviter) {
[arrayM addObject:self.signalingInfo.inviter];
}
if (self.signalingInfo.inviteeList.count > 0) {
[arrayM addObjectsFromArray:self.signalingInfo.inviteeList];
}
return [NSArray arrayWithArray:arrayM];
}
#pragma mark - Details style
- (NSString *)contentForDetailsAppearance {
TUICallProtocolType protocolType = self.protocolType;
BOOL isGroup = (self.participantType == TUICallParticipantTypeGroup);
if (protocolType == TUICallProtocolTypeUnknown) {
return TIMCommonLocalizableString(TUIkitSignalingUnrecognlize);
}
NSString *display = TIMCommonLocalizableString(TUIkitSignalingUnrecognlize);
NSString *showName = [TUIMessageBaseDataProvider getShowName:self.innerMessage];
if (protocolType == TUICallProtocolTypeSend) {
// Launch call
display = isGroup ? [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSignalingNewGroupCallFormat), showName]
: TIMCommonLocalizableString(TUIKitSignalingNewCall);
} else if (protocolType == TUICallProtocolTypeAccept) {
// Accept call
display = isGroup ? [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSignalingHangonCallFormat), showName]
: TIMCommonLocalizableString(TUIkitSignalingHangonCall);
} else if (protocolType == TUICallProtocolTypeReject) {
// Reject call
display = isGroup ? [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSignalingDeclineFormat), showName]
: TIMCommonLocalizableString(TUIkitSignalingDecline);
} else if (protocolType == TUICallProtocolTypeCancel) {
// Cancel pending call
display = isGroup ? [NSString stringWithFormat:TIMCommonLocalizableString(TUIkitSignalingCancelGroupCallFormat), showName]
: TIMCommonLocalizableString(TUIkitSignalingCancelCall);
} else if (protocolType == TUICallProtocolTypeHangup) {
// Hang up
NSUInteger duration = [[self.jsonData objectForKey:@"call_end"] unsignedIntegerValue];
display = isGroup
? TIMCommonLocalizableString(TUIKitSignalingFinishGroupChat)
: [NSString stringWithFormat:@"%@:%.2d:%.2d",TIMCommonLocalizableString(TUIKitSignalingFinishConversationAndTimeFormat),duration / 60, duration % 60];
} else if (protocolType == TUICallProtocolTypeTimeout) {
// Call timeout
NSMutableString *mutableContent = [NSMutableString string];
if (isGroup) {
for (NSString *invitee in self.signalingInfo.inviteeList) {
[mutableContent appendString:@"\"{"];
[mutableContent appendString:invitee];
[mutableContent appendString:@"}\""];
}
if (mutableContent.length > 0) {
[mutableContent replaceCharactersInRange:NSMakeRange(mutableContent.length - 1, 1) withString:@" "];
}
}
[mutableContent appendString:TIMCommonLocalizableString(TUIKitSignalingNoResponse)];
display = [NSString stringWithString:mutableContent];
} else if (protocolType == TUICallProtocolTypeLineBusy) {
// Hang up with line busy
display = isGroup ? [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSignalingBusyFormat), showName]
: TIMCommonLocalizableString(TUIKitSignalingCallBusy);
} else if (protocolType == TUICallProtocolTypeSwitchToAudio) {
// Change video-call to voice-call
display = TIMCommonLocalizableString(TUIKitSignalingSwitchToAudio);
} else if (protocolType == TUICallProtocolTypeSwitchToAudioConfirm) {
// Confirm the change of video-voice-call
display = TIMCommonLocalizableString(TUIKitSignalingComfirmSwitchToAudio);
}
return rtlString(display);
}
- (TUICallMessageDirection)directionForDetailsAppearance {
if (self.innerMessage.isSelf) {
return TUICallMessageDirectionOutgoing;
} else {
return TUICallMessageDirectionIncoming;
}
}
- (BOOL)isUseReceiverAvatarForDetailsAppearance {
return NO;
}
#pragma mark - Simplify style
- (NSString *)contentForSimplifyAppearance {
if (self.excludeFromHistory) {
return nil;
}
TUICallParticipantType participantType = self.participantType;
TUICallProtocolType protocolType = self.protocolType;
BOOL isCaller = (self.participantRole == TUICallParticipantRoleCaller);
NSString *display = nil;
NSString *showName = [TUIMessageBaseDataProvider getShowName:self.innerMessage];
if (participantType == TUICallParticipantTypeC2C) {
// C2C shown: rejectcancelhanguptimeoutline_busy
if (protocolType == TUICallProtocolTypeReject) {
display = isCaller ? TUIChatLocalizableString(TUIChatCallRejectInCaller) : TUIChatLocalizableString(TUIChatCallRejectInCallee);
} else if (protocolType == TUICallProtocolTypeCancel) {
display = isCaller ? TUIChatLocalizableString(TUIChatCallCancelInCaller) : TUIChatLocalizableString(TUIChatCallCancelInCallee);
} else if (protocolType == TUICallProtocolTypeHangup) {
NSInteger duration = [[self.jsonData objectForKey:@"call_end"] integerValue];
display = [NSString stringWithFormat:@"%@:%.2d:%.2d",TUIChatLocalizableString(TUIChatCallDurationFormat),duration / 60, duration % 60];
} else if (protocolType == TUICallProtocolTypeTimeout) {
display = isCaller ? TUIChatLocalizableString(TUIChatCallTimeoutInCaller) : TUIChatLocalizableString(TUIChatCallTimeoutInCallee);
} else if (protocolType == TUICallProtocolTypeLineBusy) {
display = isCaller ? TUIChatLocalizableString(TUIChatCallLinebusyInCaller) : TUIChatLocalizableString(TUIChatCallLinebusyInCallee);
}
// C2C compatiable
else if (protocolType == TUICallProtocolTypeSend) {
display = TUIChatLocalizableString(TUIChatCallSend);
} else if (protocolType == TUICallProtocolTypeAccept) {
display = TUIChatLocalizableString(TUIChatCallAccept);
} else if (protocolType == TUICallProtocolTypeSwitchToAudio) {
display = TUIChatLocalizableString(TUIChatCallSwitchToAudio);
} else if (protocolType == TUICallProtocolTypeSwitchToAudioConfirm) {
display = TUIChatLocalizableString(TUIChatCallConfirmSwitchToAudio);
} else {
display = TUIChatLocalizableString(TUIChatCallUnrecognized);
}
} else if (participantType == TUICallParticipantTypeGroup) {
// Group shown: invitecancelhanguptimeoutline_busy
if (protocolType == TUICallProtocolTypeSend) {
display = [NSString stringWithFormat:TUIChatLocalizableString(TUIChatGroupCallSendFormat), showName];
} else if (protocolType == TUICallProtocolTypeCancel) {
display = TUIChatLocalizableString(TUIChatGroupCallEnd);
} else if (protocolType == TUICallProtocolTypeHangup) {
display = TUIChatLocalizableString(TUIChatGroupCallEnd);
} else if (protocolType == TUICallProtocolTypeTimeout || protocolType == TUICallProtocolTypeLineBusy) {
NSMutableString *mutableContent = [NSMutableString string];
if (participantType == TUICallParticipantTypeGroup) {
for (NSString *invitee in self.signalingInfo.inviteeList) {
[mutableContent appendString:@"\"{"];
[mutableContent appendString:invitee];
[mutableContent appendString:@"}\""];
}
[mutableContent replaceCharactersInRange:NSMakeRange(mutableContent.length - 1, 1) withString:@" "];
}
if (protocolType == TUICallProtocolTypeLineBusy) {
[mutableContent appendString:TUIChatLocalizableString(TUIChatCallLinebusyInCallee)];
} else {
[mutableContent appendString:TUIChatLocalizableString(TUIChatGroupCallNoAnswer)];
}
display = [NSString stringWithString:mutableContent];
}
// Group compatiable
else if (protocolType == TUICallProtocolTypeReject) {
display = [NSString stringWithFormat:TUIChatLocalizableString(TUIChatGroupCallRejectFormat), showName];
} else if (protocolType == TUICallProtocolTypeAccept) {
display = [NSString stringWithFormat:TUIChatLocalizableString(TUIChatGroupCallAcceptFormat), showName];
} else if (protocolType == TUICallProtocolTypeSwitchToAudio) {
display = [NSString stringWithFormat:TUIChatLocalizableString(TUIChatGroupCallSwitchToAudioFormat), showName];
} else if (protocolType == TUICallProtocolTypeSwitchToAudioConfirm) {
display = [NSString stringWithFormat:TUIChatLocalizableString(TUIChatGroupCallConfirmSwitchToAudioFormat), showName];
} else {
display = TUIChatLocalizableString(TUIChatCallUnrecognized);
}
} else {
display = TUIChatLocalizableString(TUIChatCallUnrecognized);
}
return rtlString(display);
}
- (TUICallMessageDirection)directionForSimplifyAppearance {
if (self.participantRole == TUICallParticipantRoleCaller) {
return TUICallMessageDirectionOutgoing;
} else {
return TUICallMessageDirectionIncoming;
}
}
- (BOOL)isUseReceiverAvatarForSimplifyAppearance {
if (self.direction == TUICallMessageDirectionOutgoing) {
return !self.innerMessage.isSelf;
} else {
return self.innerMessage.isSelf;
}
}
#pragma mark - Utils
- (NSString *)convertProtocolTypeToString:(TUICallProtocolType)type {
static NSDictionary *dict = nil;
if (dict == nil) {
dict = @{
@(TUICallProtocolTypeSend) : @"TUICallProtocolTypeSend",
@(TUICallProtocolTypeAccept) : @"TUICallProtocolTypeAccept",
@(TUICallProtocolTypeReject) : @"TUICallProtocolTypeReject",
@(TUICallProtocolTypeCancel) : @"TUICallProtocolTypeCancel",
@(TUICallProtocolTypeHangup) : @"TUICallProtocolTypeHangup",
@(TUICallProtocolTypeTimeout) : @"TUICallProtocolTypeTimeout",
@(TUICallProtocolTypeLineBusy) : @"TUICallProtocolTypeLineBusy",
@(TUICallProtocolTypeSwitchToAudio) : @"TUICallProtocolTypeSwitchToAudio",
@(TUICallProtocolTypeSwitchToAudioConfirm) : @"TUICallProtocolTypeSwitchToAudioConfirm",
};
}
return [dict objectForKey:@(type)] ?: @"unknown";
}
@end
// ********************************************************************
// ********************************************************************
// TUIChatCallingDataProvider
// ********************************************************************
@interface TUIChatCallingDataProvider ()
@property(nonatomic, assign) TUIChatCallingMessageAppearance style;
@property(nonatomic, strong) NSCache<TUIChatMessageID, TUIChatCallingInfo *> *callingCache;
@end
@implementation TUIChatCallingDataProvider
- (instancetype)init {
if (self = [super init]) {
self.style = TUIChatCallingMessageAppearanceSimplify;
}
return self;
}
- (void)setCallingMessageStyle:(TUIChatCallingMessageAppearance)style {
self.style = style;
}
- (void)redialFromMessage:(V2TIMMessage *)innerMessage {
NSDictionary *param = nil;
id<TUIChatCallingInfoProtocol> callingInfo = nil;
if ([self isCallingMessage:innerMessage callingInfo:&callingInfo]) {
if (callingInfo.streamMediaType == TUICallStreamMediaTypeVoice) {
param = @{
TUICore_TUICallingService_ShowCallingViewMethod_UserIDsKey : @[ innerMessage.userID ],
TUICore_TUICallingService_ShowCallingViewMethod_CallTypeKey : @"0"
};
} else if (callingInfo.streamMediaType == TUICallStreamMediaTypeVideo) {
param = @{
TUICore_TUICallingService_ShowCallingViewMethod_UserIDsKey : @[ innerMessage.userID ],
TUICore_TUICallingService_ShowCallingViewMethod_CallTypeKey : @"1"
};
}
if (param) {
[TUICore callService:TUICore_TUICallingService method:TUICore_TUICallingService_ShowCallingViewMethod param:param];
}
}
}
- (BOOL)isCallingMessage:(V2TIMMessage *)innerMessage callingInfo:(id<TUIChatCallingInfoProtocol> __nullable *__nullable)callingInfo {
TUIChatCallingInfo *item = [self callingInfoForMesssage:innerMessage];
if (item == nil) {
if (callingInfo) {
*callingInfo = nil;
}
return NO;
} else {
if (callingInfo) {
*callingInfo = item;
}
return YES;
}
}
- (TUIChatCallingInfo *__nullable)callingInfoForMesssage:(V2TIMMessage *)innerMessage {
// 1. Fetch from cache
TUIChatMessageID msgID = innerMessage.msgID ?: @"";
TUIChatCallingInfo *item = [self.callingCache objectForKey:msgID];
if (item) {
item.innerMessage = innerMessage;
return item;
}
// 2. Parse
V2TIMSignalingInfo *info = [V2TIMManager.sharedInstance getSignallingInfo:innerMessage];
if (info == nil || info.data.length == 0) {
return nil;
}
NSData *data = [info.data dataUsingEncoding:NSUTF8StringEncoding];
if (data == nil) {
return nil;
}
NSError *err = nil;
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
if (param == nil || ![param isKindOfClass:NSDictionary.class]) {
return nil;
}
NSString *businessID = [param objectForKey:@"businessID"];
if (businessID == nil || ![businessID isKindOfClass:NSString.class]) {
return nil;
}
if (![businessID isEqualToString:@"av_call"] && ![businessID isEqualToString:@"rtc_call"] ) {
return nil;
}
// 3 Cached and return
item = [[TUIChatCallingInfo alloc] init];
item.style = self.style;
item.signalingInfo = info;
item.jsonData = param;
item.innerMessage = innerMessage;
[self.callingCache setObject:item forKey:msgID];
return item;
}
#pragma mark - Lazy
- (NSCache<TUIChatMessageID, TUIChatCallingInfo *> *)callingCache {
if (_callingCache == nil) {
_callingCache = [[NSCache alloc] init];
}
return _callingCache;
}
@end
// ********************************************************************