增加换肤功能
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// NSObject+Extension.h
|
||||
// TUIVideoSeat
|
||||
//
|
||||
// Created by WesleyLei on 2022/9/23.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSObject (Extension)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// NSObject+Extension.m
|
||||
// TUIVideoSeat
|
||||
//
|
||||
// Created by WesleyLei on 2022/9/23.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSObject+Extension.h"
|
||||
@implementation NSObject (Extension)
|
||||
|
||||
+ (void)load {
|
||||
#pragma GCC diagnostic ignored "-Wundeclared-selector"
|
||||
if ([self respondsToSelector:@selector(swiftLoad)]) {
|
||||
[self performSelector:@selector(swiftLoad)];
|
||||
}
|
||||
}
|
||||
@end
|
||||
42
TUIKit/TUICallKit/TUICallKit-Swift/Service/SwiftLoad.swift
Normal file
42
TUIKit/TUICallKit/TUICallKit-Swift/Service/SwiftLoad.swift
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// SwiftLoad.swift
|
||||
// TUICallKit
|
||||
//
|
||||
// Created by WesleyLei on 2022/9/23.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import TUICallEngine
|
||||
|
||||
extension NSObject {
|
||||
@objc class func swiftLoad() {
|
||||
let _ = TUICallKit.createInstance()
|
||||
let _ = TUICallEngine.createInstance()
|
||||
|
||||
TUICore.registerService(TUICore_TUICallingService, object: TUICallKitService.instance)
|
||||
|
||||
TUICore.registerService(TUICore_TUIAudioMessageRecordService, object: TUIAudioMessageRecordService.instance)
|
||||
|
||||
TUICore.registerExtension(TUICore_TUIChatExtension_NavigationMoreItem_MinimalistExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
TUICore.registerExtension(TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
TUICore.registerExtension(TUICore_TUIContactExtension_FriendProfileActionMenu_ClassicExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
TUICore.registerExtension(TUICore_TUIContactExtension_FriendProfileActionMenu_MinimalistExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
TUICore.registerExtension(TUICore_TUIContactExtension_GroupInfoCardActionMenu_MinimalistExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
|
||||
TUICore.registerExtension(TUICore_TUIChatExtension_ChatViewTopArea_ClassicExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
TUICore.registerExtension(TUICore_TUIChatExtension_ChatViewTopArea_MinimalistExtensionID,
|
||||
object: TUICallKitExtension.instance)
|
||||
|
||||
TUICore.registerObjectFactory(TUICore_TUICallingObjectFactory, objectFactory: TUICallKitObjectFactory.instance)
|
||||
|
||||
TUIThemeManager.share().registerThemeResourcePath(TUICoreDefineConvert.getTUICallKitThemePath(), for: .calling)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
//
|
||||
// TUIAudioMessageRecordService.swift
|
||||
// TUICallKit
|
||||
//
|
||||
// Created by vincepzhang on 2023/8/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFAudio
|
||||
import TUICore
|
||||
import TUICallEngine
|
||||
|
||||
#if canImport(TXLiteAVSDK_TRTC)
|
||||
import TXLiteAVSDK_TRTC
|
||||
#elseif canImport(TXLiteAVSDK_Professional)
|
||||
import TXLiteAVSDK_Professional
|
||||
#endif
|
||||
|
||||
class TUIAudioRecordInfo {
|
||||
var path: String = ""
|
||||
var sdkAppId: Int = 0
|
||||
var signature: String = ""
|
||||
}
|
||||
|
||||
class TUIAudioMessageRecordService: NSObject, TUIServiceProtocol, TUINotificationProtocol, TRTCCloudDelegate, TUICallObserver {
|
||||
|
||||
static let instance = TUIAudioMessageRecordService()
|
||||
|
||||
var audioRecordInfo: TUIAudioRecordInfo?
|
||||
var category: AVAudioSession.Category?
|
||||
var categoryOptions: AVAudioSession.CategoryOptions?
|
||||
|
||||
var callback: TUICallServiceResultCallback?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(loginSuccessNotification),
|
||||
name: NSNotification.Name.TUILoginSuccess, object: nil)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
@objc
|
||||
func loginSuccessNotification() {
|
||||
TUICallEngine.createInstance().addObserver(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TUIServiceProtocol
|
||||
extension TUIAudioMessageRecordService {
|
||||
func onCall(_ method: String, param: [AnyHashable : Any]?) -> Any? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func onCall(_ method: String, param: [AnyHashable : Any]?, resultCallback: @escaping TUICallServiceResultCallback) -> Any? {
|
||||
callback = resultCallback
|
||||
|
||||
if method == TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod {
|
||||
guard let param = param else {
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(TUICore_RecordAudioMessageNotifyError_InvalidParam), path: "")
|
||||
return nil
|
||||
}
|
||||
|
||||
if TUICallState.instance.selfUser.value.callStatus.value != TUICallStatus.none {
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(TUICore_RecordAudioMessageNotifyError_StatusInCall), path: "")
|
||||
return nil
|
||||
}
|
||||
|
||||
if audioRecordInfo != nil {
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(TUICore_RecordAudioMessageNotifyError_StatusIsAudioRecording), path: "")
|
||||
return nil
|
||||
}
|
||||
|
||||
requestRecordAuthorization { [weak self] granted in
|
||||
guard let self = self else { return }
|
||||
if granted {
|
||||
if !self.requestAudioFocus() {
|
||||
self.notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(TUICore_RecordAudioMessageNotifyError_RequestAudioFocusFailed), path: "")
|
||||
} else {
|
||||
self.audioRecordInfo = TUIAudioRecordInfo()
|
||||
let pathKey = TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod_PathKey
|
||||
self.audioRecordInfo?.path = param[pathKey] as? String ?? ""
|
||||
let sdkAppId = param[TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod_SdkappidKey] as? NSNumber
|
||||
if let sdkAppId = sdkAppId {
|
||||
self.audioRecordInfo?.sdkAppId = sdkAppId.intValue
|
||||
}
|
||||
let signatureKey = TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod_SignatureKey
|
||||
self.audioRecordInfo?.signature = param[signatureKey] as? String ?? ""
|
||||
|
||||
TRTCCloud.sharedInstance().delegate = self
|
||||
let audioVolumeEvaluateParams = TRTCAudioVolumeEvaluateParams()
|
||||
audioVolumeEvaluateParams.interval = 500
|
||||
TRTCCloud.sharedInstance().enableAudioVolumeEvaluation(true, with: audioVolumeEvaluateParams)
|
||||
self.startRecordAudioMessage()
|
||||
}
|
||||
} else {
|
||||
self.notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(TUICore_RecordAudioMessageNotifyError_MicPermissionRefused), path: "")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
} else if method == TUICore_TUIAudioMessageRecordService_StopRecordAudioMessageMethod {
|
||||
stopRecordAudioMessage()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func requestRecordAuthorization(response: @escaping (_ granted: Bool) -> Void) {
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
let permission = session.recordPermission
|
||||
|
||||
if permission == .undetermined {
|
||||
session.requestRecordPermission(response)
|
||||
} else if permission == .granted {
|
||||
response(true)
|
||||
} else if permission == .denied {
|
||||
response(false)
|
||||
}
|
||||
}
|
||||
|
||||
func startRecordAudioMessage() {
|
||||
guard let audioRecordInfo = audioRecordInfo else { return }
|
||||
|
||||
let sdkAppIdKey = TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod_SdkappidKey
|
||||
let pathKey = TUICore_TUIAudioMessageRecordService_StartRecordAudioMessageMethod_PathKey
|
||||
|
||||
let jsonParams: [String: Any] = ["api": "startRecordAudioMessage",
|
||||
"params": ["key": audioRecordInfo.signature,
|
||||
sdkAppIdKey: audioRecordInfo.sdkAppId,
|
||||
pathKey: audioRecordInfo.path,] as [String : Any],]
|
||||
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: jsonParams,
|
||||
options: JSONSerialization.WritingOptions(rawValue: 0)) else { return }
|
||||
guard let paramsString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as? String else { return }
|
||||
|
||||
TRTCCloud.sharedInstance().callExperimentalAPI(paramsString)
|
||||
}
|
||||
|
||||
func stopRecordAudioMessage() {
|
||||
guard let _ = audioRecordInfo else { return }
|
||||
|
||||
let jsonParams: [String: Any] = ["api": "stopRecordAudioMessage",
|
||||
"params": [:] as [String : Any],]
|
||||
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: jsonParams,
|
||||
options: JSONSerialization.WritingOptions(rawValue: 0)) else { return }
|
||||
guard let paramsString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as? String else { return }
|
||||
TRTCCloud.sharedInstance().callExperimentalAPI(paramsString)
|
||||
TRTCCloud.sharedInstance().enableAudioVolumeEvaluation(false, with: TRTCAudioVolumeEvaluateParams())
|
||||
TRTCCloud.sharedInstance().stopLocalAudio()
|
||||
|
||||
audioRecordInfo = nil
|
||||
|
||||
let _ = abandonAudioFocus()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TUICallObserver
|
||||
extension TUIAudioMessageRecordService {
|
||||
func onCallReceived(callerId: String, calleeIdList: [String], groupId: String?, callMediaType: TUICallMediaType, userData: String?) {
|
||||
stopRecordAudioMessage()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TRTCCloudDelegate
|
||||
extension TUIAudioMessageRecordService {
|
||||
func onError(_ errCode: TXLiteAVError, errMsg: String?, extInfo: [AnyHashable : Any]?) {
|
||||
if errCode.rawValue == TUICore_RecordAudioMessageNotifyError_MicStartFail ||
|
||||
errCode.rawValue == TUICore_RecordAudioMessageNotifyError_MicNotAuthorized ||
|
||||
errCode.rawValue == TUICore_RecordAudioMessageNotifyError_MicSetParamFail ||
|
||||
errCode.rawValue == TUICore_RecordAudioMessageNotifyError_MicOccupy {
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(errCode.rawValue) , path: "")
|
||||
}
|
||||
}
|
||||
|
||||
func onLocalRecordBegin(_ errCode: Int, storagePath: String) {
|
||||
if errCode == TUICore_RecordAudioMessageNotifyError_None {
|
||||
TRTCCloud.sharedInstance().startLocalAudio(TRTCAudioQuality.speech)
|
||||
}
|
||||
let tempCode = convertErrorCode("onLocalRecordBegin", errorCode: errCode)
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StartRecordAudioMessageSubKey,
|
||||
errorCode: Int(tempCode), path: storagePath)
|
||||
}
|
||||
|
||||
func onLocalRecordComplete(_ errCode: Int, storagePath: String) {
|
||||
let tempCode = convertErrorCode("onLocalRecordComplete", errorCode: errCode)
|
||||
notifyAudioMessageRecordEvent(method: TUICore_RecordAudioMessageNotify_StopRecordAudioMessageSubKey,
|
||||
errorCode: Int(tempCode), path: storagePath)
|
||||
}
|
||||
|
||||
func onUserVoiceVolume(_ userVolumes: [TRTCVolumeInfo], totalVolume: Int) {
|
||||
for volumeInfo in userVolumes {
|
||||
if volumeInfo.userId == nil || volumeInfo.userId?.isEmpty == true {
|
||||
let param = [TUICore_RecordAudioMessageNotify_RecordAudioVoiceVolumeSubKey_VolumeKey: volumeInfo.volume,]
|
||||
TUICore.notifyEvent(TUICore_RecordAudioMessageNotify,
|
||||
subKey: TUICore_RecordAudioMessageNotify_RecordAudioVoiceVolumeSubKey,
|
||||
object: nil, param: param)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
extension TUIAudioMessageRecordService {
|
||||
|
||||
func requestAudioFocus() -> Bool {
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
category = session.category
|
||||
categoryOptions = session.categoryOptions
|
||||
do {
|
||||
try session.setCategory(.playAndRecord, options: .allowBluetooth)
|
||||
try session.setActive(true)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func abandonAudioFocus() -> Bool {
|
||||
guard let category = category else { return false }
|
||||
guard let categoryOptions = categoryOptions else { return false }
|
||||
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
try session.setCategory(category, options: categoryOptions)
|
||||
try session.setActive(false, options: .notifyOthersOnDeactivation)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func notifyAudioMessageRecordEvent(method: String, errorCode: Int, path: String) {
|
||||
let params: [String: Any] = ["method": method,
|
||||
"errorCode": errorCode,
|
||||
"path": path,]
|
||||
|
||||
guard let callback = callback else { return }
|
||||
callback(errorCode, "", params)
|
||||
}
|
||||
|
||||
func convertErrorCode(_ method: String?, errorCode: Int) -> Int32 {
|
||||
var targetCode: Int32
|
||||
switch errorCode {
|
||||
case -1:
|
||||
if let method = method, method == "onLocalRecordBegin" {
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_RecordInitFailed
|
||||
} else {
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_RecordFailed
|
||||
}
|
||||
case -2:
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_PathFormatNotSupport
|
||||
case -3:
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_NoMessageToRecord
|
||||
case -4:
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_SignatureError
|
||||
case -5:
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_SignatureExpired
|
||||
default:
|
||||
targetCode = TUICore_RecordAudioMessageNotifyError_None
|
||||
}
|
||||
return targetCode
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// TUICallKitExtension.swift
|
||||
// TUICallKit
|
||||
//
|
||||
// Created by vincepzhang on 2023/8/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import TUICallEngine
|
||||
|
||||
class TUICallKitExtension: NSObject, TUIExtensionProtocol {
|
||||
|
||||
static let instance = TUICallKitExtension()
|
||||
var joinGroupCallViewModel = JoinInGroupCallViewModel()
|
||||
|
||||
func launchCall(type: TUICallMediaType, groupID: String, pushVC: UINavigationController, isClassic: Bool) {
|
||||
if !groupID.isEmpty {
|
||||
var requestParam: [String: Any] = [:]
|
||||
requestParam[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_GroupID] = groupID
|
||||
requestParam[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_Name] =
|
||||
TUIGlobalization.getLocalizedString(forKey: "Make-a-call", bundle: TUIKitLocalizableBundle)
|
||||
let viewControllerKey = isClassic ? TUICore_TUIContactObjectFactory_SelectGroupMemberVC_Classic :
|
||||
TUICore_TUIContactObjectFactory_SelectGroupMemberVC_Minimalist
|
||||
pushVC.push(viewControllerKey, param: requestParam) { [weak self] responseData in
|
||||
guard let self = self else { return }
|
||||
guard let modelList = responseData[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_ResultUserList]
|
||||
as? [AnyObject] else { return }
|
||||
let userIDs: [String] = modelList.compactMap { $0.userId }
|
||||
self.startCall(groupID: groupID, userIDs: userIDs, callingType: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startCall(groupID: String, userIDs: [String], callingType: TUICallMediaType) {
|
||||
let selector = NSSelectorFromString("setOnlineUserOnly")
|
||||
if TUICallEngine.createInstance().responds(to: selector) {
|
||||
TUICallEngine.createInstance().perform(selector, with: 0)
|
||||
}
|
||||
|
||||
if groupID.isEmpty {
|
||||
guard let userID = userIDs.first else { return }
|
||||
TUICallKit.createInstance().call(userId: userID, callMediaType: callingType)
|
||||
} else {
|
||||
TUICallKit.createInstance().groupCall(groupId: groupID, userIdList: userIDs, callMediaType: callingType)
|
||||
}
|
||||
}
|
||||
|
||||
func doResponseInputViewExtension(param: [String: Any], type: TUICallMediaType, isClassic: Bool) {
|
||||
let userID = param[TUICore_TUIChatExtension_InputViewMoreItem_UserID] as? String ?? ""
|
||||
let groupId = param[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] as? String ?? ""
|
||||
let pushVC = param[TUICore_TUIChatExtension_InputViewMoreItem_PushVC] as? UINavigationController
|
||||
|
||||
if !userID.isEmpty {
|
||||
startCall(groupID: "", userIDs: [userID], callingType: type)
|
||||
} else if !groupId.isEmpty {
|
||||
guard let pushVC = pushVC else { return }
|
||||
launchCall(type: type, groupID: groupId, pushVC: pushVC, isClassic: isClassic)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TUIExtensionProtocol
|
||||
func onRaiseExtension(_ extensionID: String, parentView: UIView, param: [AnyHashable : Any]?) -> Bool {
|
||||
if extensionID.isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let groupId = param?[TUICore_TUIChatExtension_ChatViewTopArea_ChatID] as? String,
|
||||
let isGroup = param?[TUICore_TUIChatExtension_ChatViewTopArea_IsGroup], isGroup as! String == "1" else {
|
||||
return false
|
||||
}
|
||||
|
||||
if extensionID == TUICore_TUIChatExtension_ChatViewTopArea_ClassicExtensionID ||
|
||||
extensionID == TUICore_TUIChatExtension_ChatViewTopArea_MinimalistExtensionID {
|
||||
let joinGroupCallView = JoinInGroupCallView()
|
||||
joinGroupCallViewModel.setJoinGroupCallView(joinGroupCallView)
|
||||
joinGroupCallViewModel.getGroupAttributes(groupId)
|
||||
parentView.subviews.forEach {
|
||||
if $0 is JoinInGroupCallView {
|
||||
$0.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
parentView.addSubview(joinGroupCallView)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func onGetExtension(_ extensionID: String, param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
if extensionID.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if extensionID == TUICore_TUIChatExtension_NavigationMoreItem_MinimalistExtensionID {
|
||||
return getNavigationMoreItemExtensionForMinimalistChat(param: param)
|
||||
} else if extensionID == TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID {
|
||||
return getInputViewMoreItemExtensionForClassicChat(param: param)
|
||||
} else if extensionID == TUICore_TUIContactExtension_FriendProfileActionMenu_ClassicExtensionID {
|
||||
return getFriendProfileActionMenuExtensionForClassicContact(param: param)
|
||||
} else if extensionID == TUICore_TUIContactExtension_FriendProfileActionMenu_MinimalistExtensionID {
|
||||
return getFriendProfileActionMenuExtensionForMinimalistContact(param: param)
|
||||
} else if extensionID == TUICore_TUIContactExtension_GroupInfoCardActionMenu_MinimalistExtensionID {
|
||||
return getGroupInfoCardActionMenuExtensionForMinimalistGroup(param: param)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func getNavigationMoreItemExtensionForMinimalistChat(param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
guard let param = param else { return nil }
|
||||
|
||||
let onClick: ([AnyHashable : Any]?, TUICallMediaType) -> Void = { [weak self] param, type in
|
||||
guard let self = self else { return }
|
||||
guard let param = param else { return }
|
||||
let userID = param[TUICore_TUIChatExtension_NavigationMoreItem_UserID] as? String ?? ""
|
||||
let groupID = param[TUICore_TUIChatExtension_NavigationMoreItem_GroupID] as? String ?? ""
|
||||
let pushVC = param[TUICore_TUIChatExtension_NavigationMoreItem_PushVC] as? UINavigationController
|
||||
|
||||
if !userID.isEmpty {
|
||||
self.startCall(groupID: "", userIDs: [userID], callingType: type)
|
||||
} else if !groupID.isEmpty {
|
||||
guard let pushVC = pushVC else { return }
|
||||
self.launchCall(type: type, groupID: groupID, pushVC: pushVC, isClassic: false)
|
||||
}
|
||||
}
|
||||
|
||||
var result: [TUIExtensionInfo] = []
|
||||
guard let filterVideoCall = param[TUICore_TUIChatExtension_NavigationMoreItem_FilterVideoCall] as? Bool else { return nil }
|
||||
|
||||
if !filterVideoCall {
|
||||
let videoInfo = TUIExtensionInfo()
|
||||
videoInfo.weight = 200
|
||||
videoInfo.icon = TUICallKitCommon.getBundleImage(name: "video_call") ?? UIImage()
|
||||
videoInfo.onClicked = { param in
|
||||
onClick(param, .video)
|
||||
}
|
||||
result.append(videoInfo)
|
||||
}
|
||||
|
||||
guard let filterAudioCall = param[TUICore_TUIChatExtension_NavigationMoreItem_FilterAudioCall] as? Bool else { return nil }
|
||||
if !filterAudioCall {
|
||||
let audioInfo = TUIExtensionInfo()
|
||||
audioInfo.weight = 100
|
||||
audioInfo.icon = TUICallKitCommon.getBundleImage(name: "audio_call") ?? UIImage()
|
||||
audioInfo.onClicked = { param in
|
||||
onClick(param, .audio)
|
||||
}
|
||||
result.append(audioInfo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getInputViewMoreItemExtensionForClassicChat(param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
guard let param = param else { return nil }
|
||||
|
||||
var result: [TUIExtensionInfo] = []
|
||||
guard let filterVideoCall = param[TUICore_TUIChatExtension_InputViewMoreItem_FilterVideoCall] as? Bool else { return nil }
|
||||
|
||||
if !filterVideoCall {
|
||||
let videoInfo = TUIExtensionInfo()
|
||||
videoInfo.weight = 600
|
||||
videoInfo.text = TUICoreDefineConvert.getTUIKitLocalizableString(key: "TUIKitMoreVideoCall")
|
||||
videoInfo.icon = TUICoreDefineConvert.getTUICoreBundleThemeImage(imageKey: "service_more_video_call_img",
|
||||
defaultImageName: "more_video_call")
|
||||
videoInfo.onClicked = { param in
|
||||
guard let param = param as? [String: Any] else { return }
|
||||
self.doResponseInputViewExtension(param: param, type: .video, isClassic: true)
|
||||
}
|
||||
result.append(videoInfo)
|
||||
}
|
||||
|
||||
guard let filterAudioCall = param[TUICore_TUIChatExtension_InputViewMoreItem_FilterAudioCall] as? Bool else { return nil }
|
||||
if !filterAudioCall {
|
||||
let audioInfo = TUIExtensionInfo()
|
||||
audioInfo.weight = 500
|
||||
audioInfo.text = TUICoreDefineConvert.getTUIKitLocalizableString(key: "TUIKitMoreVoiceCall")
|
||||
audioInfo.icon = TUICoreDefineConvert.getTUICoreBundleThemeImage(imageKey: "service_more_voice_call_img",
|
||||
defaultImageName: "more_voice_call")
|
||||
audioInfo.onClicked = { param in
|
||||
guard let param = param as? [String: Any] else { return }
|
||||
self.doResponseInputViewExtension(param: param, type: .audio, isClassic: true)
|
||||
}
|
||||
result.append(audioInfo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getFriendProfileActionMenuExtensionForClassicContact(param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
guard let param = param else { return nil }
|
||||
|
||||
var result: [TUIExtensionInfo] = []
|
||||
guard let filterVideoCall = param[TUICore_TUIContactExtension_FriendProfileActionMenu_FilterVideoCall] as? Bool else { return nil }
|
||||
if !filterVideoCall {
|
||||
let videoInfo = TUIExtensionInfo()
|
||||
videoInfo.weight = 200
|
||||
videoInfo.text = TUICoreDefineConvert.getTUIKitLocalizableString(key: "TUIKitMoreVideoCall")
|
||||
videoInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let userID = param[TUICore_TUIContactExtension_FriendProfileActionMenu_UserID] as? String else { return }
|
||||
if !userID.isEmpty {
|
||||
self.startCall(groupID: "", userIDs: [userID], callingType: .video)
|
||||
}
|
||||
}
|
||||
result.append(videoInfo)
|
||||
}
|
||||
|
||||
guard let filterAudioCall = param[TUICore_TUIContactExtension_FriendProfileActionMenu_FilterAudioCall] as? Bool else { return nil }
|
||||
if !filterAudioCall {
|
||||
let audioInfo = TUIExtensionInfo()
|
||||
audioInfo.weight = 100
|
||||
audioInfo.text = TUICoreDefineConvert.getTUIKitLocalizableString(key: "TUIKitMoreVoiceCall")
|
||||
audioInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let userID = param[TUICore_TUIContactExtension_FriendProfileActionMenu_UserID] as? String else { return }
|
||||
if !userID.isEmpty {
|
||||
self.startCall(groupID: "", userIDs: [userID], callingType: .audio)
|
||||
}
|
||||
}
|
||||
result.append(audioInfo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getFriendProfileActionMenuExtensionForMinimalistContact(param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
guard let param = param else { return nil }
|
||||
|
||||
var result: [TUIExtensionInfo] = []
|
||||
guard let filterVideoCall = param[TUICore_TUIContactExtension_FriendProfileActionMenu_FilterVideoCall] as? Bool else { return nil }
|
||||
if !filterVideoCall {
|
||||
let videoInfo = TUIExtensionInfo()
|
||||
videoInfo.weight = 100
|
||||
videoInfo.icon = TUICoreDefineConvert.getTUIDynamicImage(imageKey: "", module: TUIThemeModule.contact_Minimalist,
|
||||
defaultImage: UIImage(named: TUICoreDefineConvert.getTUIContactImagePathMinimalist(imageName: "contact_info_video")) ?? UIImage())
|
||||
videoInfo.text = TUICoreDefineConvert.getTIMCommonLocalizableString(key: "TUIKitVideo")
|
||||
videoInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let userID = param[TUICore_TUIContactExtension_FriendProfileActionMenu_UserID] as? String else { return }
|
||||
if !userID.isEmpty {
|
||||
self.startCall(groupID: "", userIDs: [userID], callingType: .video)
|
||||
}
|
||||
}
|
||||
result.append(videoInfo)
|
||||
}
|
||||
|
||||
guard let filterAudioCall = param[TUICore_TUIContactExtension_FriendProfileActionMenu_FilterAudioCall] as? Bool else { return nil }
|
||||
if !filterAudioCall {
|
||||
let audioInfo = TUIExtensionInfo()
|
||||
audioInfo.weight = 200
|
||||
audioInfo.icon = TUICoreDefineConvert.getTUIDynamicImage(imageKey: "", module: TUIThemeModule.contact_Minimalist,
|
||||
defaultImage: UIImage(named: TUICoreDefineConvert.getTUIContactImagePathMinimalist(imageName: "contact_info_audio")) ?? UIImage())
|
||||
audioInfo.text = TUICoreDefineConvert.getTIMCommonLocalizableString(key: "TUIKitAudio")
|
||||
audioInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let userID = param[TUICore_TUIContactExtension_FriendProfileActionMenu_UserID] as? String else { return }
|
||||
if !userID.isEmpty {
|
||||
self.startCall(groupID: "", userIDs: [userID], callingType: .audio)
|
||||
}
|
||||
}
|
||||
result.append(audioInfo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getGroupInfoCardActionMenuExtensionForMinimalistGroup(param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
guard let param = param else { return nil }
|
||||
|
||||
var result: [TUIExtensionInfo] = []
|
||||
guard let filterVideoCall = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_FilterVideoCall] as? Bool else { return nil }
|
||||
if !filterVideoCall {
|
||||
let videoInfo = TUIExtensionInfo()
|
||||
videoInfo.weight = 100
|
||||
videoInfo.icon = TUICoreDefineConvert.getTUIDynamicImage(imageKey: "", module: TUIThemeModule.contact_Minimalist,
|
||||
defaultImage: UIImage(named: TUICoreDefineConvert.getTUIContactImagePathMinimalist(imageName: "contact_info_video")) ?? UIImage())
|
||||
videoInfo.text = TUICoreDefineConvert.getTIMCommonLocalizableString(key: "TUIKitVideo")
|
||||
videoInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let pushVC = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_PushVC] as? UINavigationController else { return }
|
||||
guard let groupID = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_GroupID] as? String else { return }
|
||||
if !groupID.isEmpty {
|
||||
self.launchCall(type: .video, groupID: groupID, pushVC: pushVC, isClassic: false)
|
||||
}
|
||||
}
|
||||
result.append(videoInfo)
|
||||
}
|
||||
|
||||
guard let filterAudioCall = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_FilterAudioCall] as? Bool else { return nil }
|
||||
if !filterAudioCall {
|
||||
let audioInfo = TUIExtensionInfo()
|
||||
audioInfo.weight = 200
|
||||
audioInfo.icon = TUICoreDefineConvert.getTUIDynamicImage(imageKey: "", module: TUIThemeModule.contact_Minimalist,
|
||||
defaultImage: UIImage(named: TUICoreDefineConvert.getTUIContactImagePathMinimalist(imageName: "contact_info_audio")) ?? UIImage())
|
||||
audioInfo.text = TUICoreDefineConvert.getTIMCommonLocalizableString(key: "TUIKitAudio")
|
||||
audioInfo.onClicked = {[weak self] param in
|
||||
guard let self = self else { return }
|
||||
guard let pushVC = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_PushVC] as? UINavigationController else { return }
|
||||
guard let groupID = param[TUICore_TUIContactExtension_GroupInfoCardActionMenu_GroupID] as? String else { return }
|
||||
if !groupID.isEmpty {
|
||||
self.launchCall(type: .audio, groupID: groupID, pushVC: pushVC, isClassic: false)
|
||||
}
|
||||
}
|
||||
result.append(audioInfo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// TUICallKitObjectFactory.swift
|
||||
// TUICallKit
|
||||
//
|
||||
// Created by vincepzhang on 2023/8/15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class TUICallKitObjectFactory: NSObject, TUIObjectProtocol {
|
||||
|
||||
static let instance = TUICallKitObjectFactory()
|
||||
|
||||
func createRecordCallsVC(uiStyle: String) -> UIViewController {
|
||||
|
||||
var recordCallsUIStyle: TUICallKitRecordCallsUIStyle = .minimalist
|
||||
|
||||
if uiStyle == TUICore_TUICallingObjectFactory_RecordCallsVC_UIStyle_Classic {
|
||||
recordCallsUIStyle = .classic
|
||||
}
|
||||
|
||||
let vc = TUICallRecordCallsViewController(recordCallsUIStyle: recordCallsUIStyle)
|
||||
return vc
|
||||
}
|
||||
|
||||
// MARK: TUIObjectProtocol
|
||||
func onCreateObject(_ method: String, param: [AnyHashable : Any]?) -> Any? {
|
||||
if method == TUICore_TUICallingObjectFactory_RecordCallsVC {
|
||||
guard let uiStyle = param?[TUICore_TUICallingObjectFactory_RecordCallsVC_UIStyle] as? String else { return nil }
|
||||
return createRecordCallsVC(uiStyle: uiStyle)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// TUICallKitService.swift
|
||||
// TUICallKit
|
||||
//
|
||||
// Created by vincepzhang on 2023/4/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import TUICallEngine
|
||||
import UIKit
|
||||
|
||||
class TUICallKitService: NSObject, TUIServiceProtocol {
|
||||
static let instance = TUICallKitService()
|
||||
|
||||
func startCall(groupID: String, userIDs: [String], callingType: TUICallMediaType) {
|
||||
let selector = NSSelectorFromString("setOnlineUserOnly")
|
||||
if TUICallEngine.createInstance().responds(to: selector) {
|
||||
TUICallEngine.createInstance().perform(selector, with: 0)
|
||||
}
|
||||
|
||||
if groupID.isEmpty {
|
||||
guard let userID = userIDs.first else {
|
||||
return
|
||||
}
|
||||
TUICallKit.createInstance().call(userId: userID, callMediaType: callingType)
|
||||
} else {
|
||||
TUICallKit.createInstance().groupCall(groupId: groupID, userIdList: userIDs, callMediaType: callingType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: TUIServiceProtocol
|
||||
extension TUICallKitService {
|
||||
func onCall(_ method: String, param: [AnyHashable : Any]?) -> Any? {
|
||||
guard let param = param else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if method == TUICore_TUICallingService_EnableFloatWindowMethod {
|
||||
guard let enableFloatWindow = param[TUICore_TUICallingService_EnableFloatWindowMethod_EnableFloatWindow] as? Bool else {
|
||||
return nil
|
||||
}
|
||||
TUICallKit.createInstance().enableFloatWindow(enable: enableFloatWindow)
|
||||
} else if method == TUICore_TUICallingService_EnableIncomingBannerMethod {
|
||||
guard let enableIncomingBanner = param[TUICore_TUICallingService_EnableIncomingBannerMethod_EnableIncomingBanner] as? Bool else {
|
||||
return nil
|
||||
}
|
||||
TUICallKit.createInstance().enableIncomingBanner(enable: enableIncomingBanner)
|
||||
} else if method == TUICore_TUICallingService_EnableVirtualBackgroundForCallMethod {
|
||||
guard let enableVirtualBackground = param[TUICore_TUICallingService_EnableVirtualBackgroundForCallMethod_EnableVirtualBackgroundForCall] as? Bool else {
|
||||
return nil
|
||||
}
|
||||
TUICallKit.createInstance().enableVirtualBackground(enable: enableVirtualBackground)
|
||||
} else if method == TUICore_TUICallingService_ShowCallingViewMethod {
|
||||
guard let userIDs = param[TUICore_TUICallingService_ShowCallingViewMethod_UserIDsKey] as? [ String],
|
||||
let mediaTypeIndex = param[TUICore_TUICallingService_ShowCallingViewMethod_CallTypeKey] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var mediaType: TUICallMediaType = .unknown
|
||||
if mediaTypeIndex == "0" {
|
||||
mediaType = .audio
|
||||
} else if mediaTypeIndex == "1" {
|
||||
mediaType = .video
|
||||
}
|
||||
let groupId = param[TUICore_TUICallingService_ShowCallingViewMethod_GroupIDKey] as? String ?? ""
|
||||
startCall(groupID: groupId, userIDs: userIDs, callingType: mediaType)
|
||||
} else if method == TUICore_TUICallingService_ReceivePushCallingMethod {
|
||||
guard let signalingInfo = param[TUICore_TUICallingService_ShowCallingViewMethod_SignalingInfo] as? V2TIMSignalingInfo else {
|
||||
return nil
|
||||
}
|
||||
let selector = NSSelectorFromString("onReceiveGroupCallAPNs:")
|
||||
if TUICallEngine.createInstance().responds(to: selector) {
|
||||
TUICallEngine.createInstance().perform(selector, with: signalingInfo)
|
||||
}
|
||||
} else if method == TUICore_TUICallingService_EnableMultiDeviceAbilityMethod {
|
||||
let key = TUICore_TUICallingService_EnableMultiDeviceAbilityMethod_EnableMultiDeviceAbility
|
||||
guard let enableMultiDeviceAbility = param[key] as? Bool else {
|
||||
return nil
|
||||
}
|
||||
TUICallEngine.createInstance().enableMultiDeviceAbility(enable: enableMultiDeviceAbility) {
|
||||
|
||||
} fail: { code, message in
|
||||
|
||||
}
|
||||
} else {
|
||||
CallEngineManager.instance.voipDataSyncHandler.onCall(method, param: param)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// VoIPDataSyncHandler.swift
|
||||
// Pods
|
||||
//
|
||||
// Created by vincepzhang on 2024/11/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import TUICallEngine
|
||||
import UIKit
|
||||
|
||||
class VoIPDataSyncHandler {
|
||||
|
||||
func onCall(_ method: String, param: [AnyHashable : Any]?) {
|
||||
guard let param = param else { return }
|
||||
|
||||
if method == TUICore_TUICallingService_SetAudioPlaybackDeviceMethod {
|
||||
let key = TUICore_TUICallingService_SetAudioPlaybackDevice_AudioPlaybackDevice
|
||||
guard let value = param[key] as? UInt else { return }
|
||||
let audioPlaybackDevice: TUIAudioPlaybackDevice
|
||||
switch value {
|
||||
case TUIAudioPlaybackDevice.earpiece.rawValue:
|
||||
audioPlaybackDevice = .earpiece
|
||||
default:
|
||||
audioPlaybackDevice = .speakerphone
|
||||
}
|
||||
|
||||
Logger.info("VoIPDataSyncHandler - onCall - selectAudioPlaybackDevice. deivce:\(audioPlaybackDevice)")
|
||||
CallEngineManager.instance.selectAudioPlaybackDevice(device: audioPlaybackDevice)
|
||||
} else if method == TUICore_TUICallingService_SetIsMicMuteMethod {
|
||||
guard let isMicMute = param[TUICore_TUICallingService_SetIsMicMuteMethod_IsMicMute] as? Bool else {
|
||||
return
|
||||
}
|
||||
|
||||
if isMicMute {
|
||||
Logger.info("VoIPDataSyncHandler - onCall - closeMicrophone")
|
||||
CallEngineManager.instance.closeMicrophone(false)
|
||||
} else {
|
||||
Logger.info("VoIPDataSyncHandler - onCall - openMicrophone")
|
||||
CallEngineManager.instance.openMicrophone(false)
|
||||
}
|
||||
} else if method == TUICore_TUICallingService_HangupMethod {
|
||||
if TUICallState.instance.selfUser.value.callStatus.value == .accept {
|
||||
Logger.info("VoIPDataSyncHandler - onCall - hangup")
|
||||
CallEngineManager.instance.hangup()
|
||||
} else {
|
||||
Logger.info("VoIPDataSyncHandler - onCall - reject")
|
||||
CallEngineManager.instance.reject()
|
||||
}
|
||||
} else if method == TUICore_TUICallingService_AcceptMethod {
|
||||
Logger.info("VoIPDataSyncHandler - onCall - accept")
|
||||
CallEngineManager.instance.accept()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func setVoIPMuteForTUICallKitVoIPExtension(_ mute: Bool) {
|
||||
TUICore.notifyEvent(TUICore_TUICallKitVoIPExtensionNotify,
|
||||
subKey: mute ? TUICore_TUICore_TUICallKitVoIPExtensionNotify_CloseMicrophoneSubKey :
|
||||
TUICore_TUICore_TUICallKitVoIPExtensionNotify_OpenMicrophoneSubKey,
|
||||
object: nil,
|
||||
param: nil)
|
||||
}
|
||||
|
||||
func setVoIPMute(_ mute: Bool) {
|
||||
Logger.info("VoIPDataSyncHandler - setVoIPMute. mute:\(mute)")
|
||||
|
||||
TUICore.notifyEvent(TUICore_TUIVoIPExtensionNotify,
|
||||
subKey: TUICore_TUICore_TUIVoIPExtensionNotify_MuteSubKey,
|
||||
object: nil,
|
||||
param: [TUICore_TUICore_TUIVoIPExtensionNotify_MuteSubKey_IsMuteKey: mute])
|
||||
|
||||
}
|
||||
|
||||
func closeVoIP() {
|
||||
Logger.info("VoIPDataSyncHandler - closeVoIP")
|
||||
TUICore.notifyEvent(TUICore_TUIVoIPExtensionNotify,
|
||||
subKey: TUICore_TUICore_TUIVoIPExtensionNotify_EndSubKey,
|
||||
object: nil,
|
||||
param: nil)
|
||||
}
|
||||
|
||||
func callBegin() {
|
||||
Logger.info("VoIPDataSyncHandler - callBegin")
|
||||
TUICore.notifyEvent(TUICore_TUIVoIPExtensionNotify,
|
||||
subKey: TUICore_TUICore_TUIVoIPExtensionNotify_ConnectedKey,
|
||||
object: nil,
|
||||
param: nil)
|
||||
}
|
||||
|
||||
func updateVoIPInfo(callerId: String, calleeList: [String], groupId: String) {
|
||||
Logger.info("VoIPDataSyncHandler - updateInfo")
|
||||
TUICore.notifyEvent(TUICore_TUIVoIPExtensionNotify,
|
||||
subKey: TUICore_TUICore_TUIVoIPExtensionNotify_UpdateInfoSubKey,
|
||||
object: nil,
|
||||
param: [TUICore_TUICore_TUIVoIPExtensionNotify_UpdateInfoSubKey_InviterIdKey: callerId,
|
||||
TUICore_TUICore_TUIVoIPExtensionNotify_UpdateInfoSubKey_InviteeListKey: calleeList,
|
||||
TUICore_TUICore_TUIVoIPExtensionNotify_UpdateInfoSubKey_GroupIDKey: groupId])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user