This commit is contained in:
启星
2025-08-08 10:49:36 +08:00
parent 6400cf78bb
commit b5ce3d580a
8780 changed files with 978183 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
//
// Constants.swift
// TUICallKit
//
// Created by vincepzhang on 2022/12/30.
//
// Default avatar
let TUI_CALL_DEFAULT_AVATAR: String = "https://imgcache.qq.com/qcloud/public/static//avatar1_100.20191230.png"
// MARK: Screen Size Param
let ScreenSize = UIScreen.main.bounds.size
let Screen_Width = UIScreen.main.bounds.size.width
let Screen_Height = UIScreen.main.bounds.size.height
let StatusBar_Height: CGFloat = {
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
statusBarHeight = UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
statusBarHeight = UIApplication.shared.statusBarFrame.height
}
return statusBarHeight
}()
let Bottom_SafeHeight = {var bottomSafeHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows.first
bottomSafeHeight = window?.safeAreaInsets.bottom ?? 0
}
return bottomSafeHeight
}()
// MARK: FloatWindow UI Param
let kMicroAudioViewWidth = 88.scaleWidth()
let kMicroAudioViewHeight = 88.scaleWidth()
let kMicroVideoViewWidth = 110.scaleWidth()
let kMicroVideoViewHeight = 196.scaleHeight()
let kMicroGroupViewWidth = 88.scaleWidth()
let kMicroGroupViewHeight = 106.scaleWidth()
let kMicroAudioViewRect = CGRect(x: Screen_Width - kMicroAudioViewWidth,
y: 150.scaleHeight(),
width: kMicroAudioViewWidth,
height: kMicroAudioViewHeight)
let kMicroVideoViewRect = CGRect(x: Screen_Width - kMicroVideoViewWidth,
y: 150.scaleHeight(),
width: kMicroVideoViewWidth,
height: kMicroVideoViewHeight)
let kMicroGroupViewRect = CGRect(x: Screen_Width - kMicroGroupViewWidth,
y: 150.scaleHeight(),
width: kMicroGroupViewWidth,
height: kMicroGroupViewHeight)
// MARK: UI Size Param
let kFloatWindowButtonSize = CGSize(width: 30, height: 30)
let kInviteUserButtonSize = CGSize(width: 30, height: 30)
// MARK: TUICore & IM
let TMoreCell_Image_Size = CGSize(width: 65, height: 65)
let TUI_CALLING_BELL_KEY = "CallingBell"
let MAX_USER = 9
let ENABLE_MUTEMODE_USERDEFAULT = "ENABLE_MUTEMODE_USERDEFAULT"
let TUI_CALLKIT_SIGNALING_MAX_TIME : Int32 = 30
let kControlBtnSize = CGSize(width: 100.scaleWidth(), height: 94.scaleWidth())
let kBtnLargeSize = CGSize(width: 64.scaleWidth(), height: 64.scaleWidth())
let kBtnSmallSize = CGSize(width: 60.scaleWidth(), height: 60.scaleWidth())
class Constants {
static let EVENT_SHOW_TUICALLKIT_VIEWCONTROLLER = "eventShowTUICallKitViewController"
}
enum NetworkQualityHint {
case None
case Local
case Remote
}

View File

@@ -0,0 +1,29 @@
//
// OfflinePushInfoConfig.swift
// TUICallKit
//
// Created by vincepzhang on 2023/1/6.
//
import Foundation
import TUICallEngine
@objc
public class OfflinePushInfoConfig: NSObject {
@objc
public static func createOfflinePushInfo() -> TUIOfflinePushInfo {
let pushInfo: TUIOfflinePushInfo = TUIOfflinePushInfo()
pushInfo.title = ""
pushInfo.desc = TUICallKitLocalize(key: "TUICallKit.have.new.invitation") ?? ""
// iOS push type: if you want user VoIP, please modify type to TUICallIOSOfflinePushTypeVoIP
pushInfo.iOSPushType = .apns
pushInfo.ignoreIOSBadge = false
pushInfo.iOSSound = "phone_ringing.mp3"
pushInfo.androidSound = "phone_ringing"
// VIVO message type: 0-push message, 1-System message(have a higher delivery rate)
pushInfo.androidVIVOClassification = 1
// HuaWei message type: https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/message-classification-0000001149358835
pushInfo.androidHuaWeiCategory = "IM"
return pushInfo
}
}

View File

@@ -0,0 +1,95 @@
//
// User.swift
// TUICallKit
//
// Created by vincepzhang on 2023/1/6.
//
import Foundation
import ImSDK_Plus
import TUICallEngine
import TUICore
enum UserUpdate {
case delete(Int)
case insert(User, Int)
case move(Int, Int)
}
class User {
let id: Observable<String> = Observable("")
let nickname: Observable<String> = Observable("")
let avatar: Observable<String> = Observable("")
let remark: Observable<String> = Observable("")
let callRole: Observable<TUICallRole> = Observable(.none)
let callStatus: Observable<TUICallStatus> = Observable(.none)
let audioAvailable: Observable<Bool> = Observable(false)
let videoAvailable: Observable<Bool> = Observable(false)
let playoutVolume: Observable<Float> = Observable(0)
let networkQualityReminder: Observable<Bool> = Observable(false)
var index: Int = -1
static func convertUserFromImFullInfo(user: V2TIMGroupMemberFullInfo) -> User {
let dstUser = User()
dstUser.nickname.value = user.nickName ?? ""
dstUser.avatar.value = user.faceURL ?? ""
dstUser.id.value = user.userID ?? ""
return dstUser
}
static func convertUser(user: V2TIMUserInfo) -> User {
return self.convertUser(user: user, volume: 0)
}
static func convertUser(user: V2TIMUserInfo, volume: Float) -> User {
let dstUser = User()
dstUser.nickname.value = user.nickName ?? ""
dstUser.avatar.value = user.faceURL ?? ""
dstUser.id.value = user.userID ?? ""
dstUser.playoutVolume.value = volume
return dstUser
}
static func getUserInfosFromIM(userIDs: [String], response: @escaping ([User]) -> Void ) {
V2TIMManager.sharedInstance().getFriendsInfo(userIDs) { friendInfosOptional in
guard let friendInfos = friendInfosOptional else { return }
var userModels: [User] = Array()
for friendInfo in friendInfos {
let userModel = convertUser(user: friendInfo.friendInfo.userFullInfo)
userModel.remark.value = friendInfo.friendInfo.friendRemark ?? ""
userModels.append(userModel)
}
response(userModels)
} fail: { code, message in
print("getUsersInfo file code:\(code) message:\(message ?? "") ")
}
}
static func getSelfUserInfo(response: @escaping (User) -> Void ){
guard let selfId = TUILogin.getUserID() else { return }
var selfInfo = User()
User.getUserInfosFromIM(userIDs: [selfId]) { users in
if let user = users.first {
selfInfo = user
response(selfInfo)
}
}
}
static func getUserDisplayName(user: User) -> String {
if !user.remark.value.isEmpty {
return user.remark.value
}
if !user.nickname.value.isEmpty {
return user.nickname.value
}
return user.id.value
}
}

View File

@@ -0,0 +1,123 @@
//
// GenerateTestUserSig.swift
// TUICallKitApp
//
// Created by abyyxwang on 2021/5/7.
// Copyright © 2021 Tencent. All rights reserved.
//
import Foundation
import CommonCrypto
import zlib
import TUICore
@objc
public class GenerateTestUserSig: NSObject {
@objc
public class func genTestUserSig(userID: String, sdkAppID: Int, secretKey: String) -> String {
// Signature expiration time, it is recommended not to set it too short.
// Default time: 7 x 24 x 60 x 60 = 604800 = 7 days
let EXPIRETIME = 604_800
let current = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970
let TLSTime: CLong = CLong(floor(current))
var obj: [String: Any] = [
"TLS.ver": "2.0",
"TLS.identifier": userID,
"TLS.sdkappid": sdkAppID,
"TLS.expire": EXPIRETIME,
"TLS.time": TLSTime,
]
let keyOrder = [
"TLS.identifier",
"TLS.sdkappid",
"TLS.time",
"TLS.expire",
]
var stringToSign = ""
keyOrder.forEach { (key) in
if let value = obj[key] {
stringToSign += "\(key):\(value)\n"
}
}
print("string to sign: \(stringToSign)")
guard var sig = hmac(plainText: stringToSign, secretKey: secretKey) else {
print("hmac error: \(stringToSign)")
return ""
}
obj["TLS.sig"] = sig
print("sig: \(String(describing: sig))")
guard let jsonData = try? JSONSerialization.data(withJSONObject: obj, options: .sortedKeys) else {
print("jsonData error: \(obj)")
return ""
}
let bytes = jsonData.withUnsafeBytes { (result) -> UnsafePointer<Bytef>? in
return result.bindMemory(to: Bytef.self).baseAddress
}
let srcLen: uLongf = uLongf(jsonData.count)
let upperBound: uLong = compressBound(srcLen)
let capacity: Int = Int(upperBound)
let dest: UnsafeMutablePointer<Bytef> = UnsafeMutablePointer<Bytef>.allocate(capacity: capacity)
var destLen = upperBound
let ret = compress2(dest, &destLen, bytes, srcLen, Z_BEST_SPEED)
if ret != Z_OK {
print("[Error] Compress Error \(ret), upper bound: \(upperBound)")
dest.deallocate()
return ""
}
let count = Int(destLen)
let result = self.base64URL(data: Data(bytesNoCopy: dest, count: count, deallocator: .free))
return result
}
class func hmac(plainText: String, secretKey: String) -> String? {
guard let cKey = secretKey.cString(using: String.Encoding.ascii) else {
print("hmac secretKey error: \(secretKey)")
return nil
}
print("hmac secretKey: \(secretKey)")
print("hmac cKey: \(cKey)")
guard let cData = plainText.cString(using: String.Encoding.ascii) else{
print("hmac plainText error: \(plainText)")
return nil
}
print("hmac plainText: \(plainText)")
print("hmac cData: \(cData)")
let cKeyLen = secretKey.lengthOfBytes(using: .ascii)
let cDataLen = plainText.lengthOfBytes(using: .ascii)
var cHMAC = [CUnsignedChar].init(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
let pointer = cHMAC.withUnsafeMutableBufferPointer { (unsafeBufferPointer) in
return unsafeBufferPointer
}
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), cKey, cKeyLen, cData, cDataLen, pointer.baseAddress)
guard let adress = pointer.baseAddress else {
print("adress error: \(String(describing: pointer))")
return nil
}
let data = Data(bytes: adress, count: cHMAC.count)
print("cHMAC.count: \(String(describing: cHMAC.count))")
print("data: \(String(describing: data))")
let result = data.base64EncodedString(options: [])
return result
}
class func base64URL(data: Data) -> String {
let result = data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
var final = ""
result.forEach { (char) in
switch char {
case "+":
final += "*"
case "/":
final += "-"
case "=":
final += "_"
default:
final += "\(char)"
}
}
return final
}
}

View File

@@ -0,0 +1,200 @@
//
// CallingBellFeature.swift
// TUICallKit
//
// Created by vincepzhang on 2022/12/30.
//
import Foundation
import AVFAudio
import TUICallEngine
#if canImport(TXLiteAVSDK_TRTC)
import TXLiteAVSDK_TRTC
#elseif canImport(TXLiteAVSDK_Professional)
import TXLiteAVSDK_Professional
#endif
let CALLKIT_AUDIO_DIAL_ID: Int32 = 48
class CallingBellFeature: NSObject, AVAudioPlayerDelegate {
enum CallingBellType {
case CallingBellTypeHangup
case CallingBellTypeCalled
case CallingBellTypeDial
}
var player: AVAudioPlayer?
var loop: Bool = true
private var needPlayRingtone: Bool = false
let selfUserCallStatusObserver = Observer()
override init() {
super.init()
registerNotifications()
registerObserveState()
}
deinit {
NotificationCenter.default.removeObserver(self)
TUICallState.instance.selfUser.value.callStatus.removeObserver(selfUserCallStatusObserver)
}
func registerObserveState() {
TUICallState.instance.selfUser.value.callStatus.addObserver(selfUserCallStatusObserver, closure: { newValue, _ in
let callStatus = TUICallState.instance.selfUser.value.callStatus.value
let callRole = TUICallState.instance.selfUser.value.callRole.value
if callStatus == TUICallStatus.waiting {
if callRole == TUICallRole.called {
self.startMusicBasedOnAppState(type: .CallingBellTypeCalled)
} else if callRole == TUICallRole.call {
self.startPlayMusic(type: .CallingBellTypeDial)
}
} else {
self.stopPlayMusic()
}
})
}
func startMusicBasedOnAppState(type: CallingBellType) {
if UIApplication.shared.applicationState != .background {
self.setAudioSessionWith(category: .soloAmbient)
self.startPlayMusic(type: type)
} else {
self.needPlayRingtone = true;
}
}
func registerNotifications() {
if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive),
name: UIScene.didActivateNotification,
object: nil)
} else {
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)
}
}
@objc func appDidBecomeActive() {
if needPlayRingtone {
let _ = startPlayMusic(type: .CallingBellTypeCalled)
needPlayRingtone = false;
}
}
func startPlayMusic(type: CallingBellType) {
guard let bundle = TUICallKitCommon.getTUICallKitBundle() else { return }
switch type {
case .CallingBellTypeHangup:
let path = bundle.bundlePath + "/AudioFile" + "/phone_hangup.mp3"
let url = URL(fileURLWithPath: path)
return startPlayMusicBySystemPlayer(url: url, loop: false)
case .CallingBellTypeCalled:
if TUICallState.instance.enableMuteMode {
return
}
var path = bundle.bundlePath + "/AudioFile" + "/phone_ringing.mp3"
if let value = UserDefaults.standard.object(forKey: TUI_CALLING_BELL_KEY) as? String {
path = value
}
let url = URL(fileURLWithPath: path)
return startPlayMusicBySystemPlayer(url: url)
case .CallingBellTypeDial:
let path = bundle.bundlePath + "/AudioFile" + "/phone_dialing.m4a"
startPlayMusicByTRTCPlayer(path: path, id: CALLKIT_AUDIO_DIAL_ID)
return
}
}
func stopPlayMusic() {
setAudioSessionWith(category: .playAndRecord)
if TUICallState.instance.selfUser.value.callRole.value == .call {
stopPlayMusicByTRTCPlayer(id: CALLKIT_AUDIO_DIAL_ID)
return
}
stopPlayMusicBySystemPlayer()
needPlayRingtone = false;
}
// MARK: TRTC Audio Player
private func startPlayMusicByTRTCPlayer(path: String, id: Int32) {
let audioDevice: TUIAudioPlaybackDevice = TUICallState.instance.mediaType.value == .audio ? .earpiece : .speakerphone
CallEngineManager.instance.setAudioPlaybackDevice(device: audioDevice)
let param = TXAudioMusicParam()
param.id = id
param.isShortFile = true
param.path = path
let audioEffectManager = TUICallEngine.createInstance().getTRTCCloudInstance().getAudioEffectManager()
audioEffectManager.startPlayMusic(param, onStart: nil, onProgress: nil)
audioEffectManager.setMusicPlayoutVolume(id, volume: 100)
}
private func stopPlayMusicByTRTCPlayer(id: Int32) {
let audioEffectManager = TUICallEngine.createInstance().getTRTCCloudInstance().getAudioEffectManager()
audioEffectManager.stopPlayMusic(id)
}
// MARK: System AVAudio Player
private func startPlayMusicBySystemPlayer(url: URL, loop: Bool = true) {
self.loop = loop
if player != nil {
stopPlayMusicBySystemPlayer()
}
do {
player = try AVAudioPlayer(contentsOf: url)
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
return
}
guard let prepare = player?.prepareToPlay(), prepare else {
return
}
player?.delegate = self
player?.play()
}
private func stopPlayMusicBySystemPlayer() {
player?.stop()
player = nil
}
// MARK: AVAudioPlayerDelegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if loop {
player.play()
} else {
stopPlayMusicBySystemPlayer()
}
}
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
if error != nil {
stopPlayMusicBySystemPlayer()
}
}
private func setAudioSessionWith(category: AVAudioSession.Category) {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(category, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
try audioSession.setActive(true)
} catch {
print("Error setting up audio session: \(error)")
}
}
}

View File

@@ -0,0 +1,55 @@
//
// CallingVibrator.swift
// TUICallKit-Swift
//
// Created by iveshe on 2024/12/31.
//
import AudioToolbox
import TUICallEngine
class CallingVibratorFeature: NSObject {
private static var isVibrating = false;
let selfUserCallStatusObserver = Observer()
override init() {
super.init()
registerObserveState()
}
deinit {
TUICallState.instance.selfUser.value.callStatus.removeObserver(selfUserCallStatusObserver)
}
func registerObserveState() {
TUICallState.instance.selfUser.value.callStatus.addObserver(selfUserCallStatusObserver, closure: { newValue, _ in
let callStatus = TUICallState.instance.selfUser.value.callStatus.value
let callRole = TUICallState.instance.selfUser.value.callRole.value
if callStatus == TUICallStatus.waiting && callRole == TUICallRole.called {
CallingVibratorFeature.startVibration()
} else {
CallingVibratorFeature.stopVibration()
}
})
}
static func startVibration() {
isVibrating = true
vibrate()
}
private static func vibrate() {
guard isVibrating else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
vibrate()
}
}
static func stopVibration() {
isVibrating = false;
}
}

View File

@@ -0,0 +1,390 @@
//
// CallEngineManager.swift
// TUICallKit
//
// Created by vincepzhang on 2023/1/6.
//
import Foundation
import TUICore
import TUICallEngine
#if canImport(TXLiteAVSDK_TRTC)
import TXLiteAVSDK_TRTC
#elseif canImport(TXLiteAVSDK_Professional)
import TXLiteAVSDK_Professional
#endif
class CallEngineManager {
static let instance = CallEngineManager()
let voipDataSyncHandler = VoIPDataSyncHandler()
func setSelfInfo(nickname: String, avatar: String, succ: @escaping TUICallSucc, fail: @escaping TUICallFail) {
TUICallEngine.createInstance().setSelfInfo(nickname: nickname, avatar: avatar) {
TUICallState.instance.selfUser.value.avatar.value = avatar
TUICallState.instance.selfUser.value.nickname.value = nickname
succ()
} fail: { code, message in
fail(code, message)
}
}
func call(userId: String, callMediaType: TUICallMediaType, params: TUICallParams, succ: @escaping TUICallSucc, fail: @escaping TUICallFail) {
TUICallEngine.createInstance().call(userId: userId, callMediaType: callMediaType, params: params) {
User.getUserInfosFromIM(userIDs: [userId]) { mInviteeList in
TUICallState.instance.remoteUserList.value = mInviteeList
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
TUICallState.instance.remoteUserList.value[index].callStatus.value = TUICallStatus.waiting
TUICallState.instance.remoteUserList.value[index].callRole.value = TUICallRole.called
}
}
TUICallState.instance.mediaType.value = callMediaType
TUICallState.instance.scene.value = TUICallScene.single
TUICallState.instance.selfUser.value.callRole.value = TUICallRole.call
TUICallState.instance.selfUser.value.callStatus.value = TUICallStatus.waiting
if callMediaType == .audio {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.earpiece
TUICallState.instance.isCameraOpen.value = false
} else if callMediaType == .video {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.speakerphone
TUICallState.instance.isCameraOpen.value = true
}
succ()
} fail: { code, message in
fail(code,message)
}
}
func groupCall(groupId: String,
userIdList: [String],
callMediaType: TUICallMediaType,
params: TUICallParams,
succ: @escaping TUICallSucc,
fail: @escaping TUICallFail) {
TUICallEngine.createInstance().groupCall(groupId: groupId,
userIdList: userIdList,
callMediaType: callMediaType,
params: params) {
TUICallState.instance.groupId.value = groupId
User.getUserInfosFromIM(userIDs: userIdList) { mInviteeList in
TUICallState.instance.remoteUserList.value = mInviteeList
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
TUICallState.instance.remoteUserList.value[index].callStatus.value = TUICallStatus.waiting
TUICallState.instance.remoteUserList.value[index].callRole.value = TUICallRole.called
}
}
TUICallState.instance.mediaType.value = callMediaType
TUICallState.instance.scene.value = TUICallScene.group
TUICallState.instance.selfUser.value.callRole.value = TUICallRole.call
TUICallState.instance.selfUser.value.callStatus.value = TUICallStatus.waiting
if callMediaType == .audio {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.earpiece
TUICallState.instance.isCameraOpen.value = false
} else if callMediaType == .video {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.speakerphone
TUICallState.instance.isCameraOpen.value = true
}
succ()
} fail: { code, message in
fail(code, message)
}
}
func joinInGroupCall(roomId: TUIRoomId,
groupId: String,
callMediaType: TUICallMediaType,
succ: @escaping TUICallSucc,
fail: @escaping TUICallFail) {
TUICallEngine.createInstance().joinInGroupCall(roomId: roomId, groupId: groupId, callMediaType: callMediaType) {
TUICallState.instance.mediaType.value = callMediaType
TUICallState.instance.scene.value = TUICallScene.group
TUICallState.instance.groupId.value = groupId
TUICallState.instance.selfUser.value.callRole.value = TUICallRole.called
TUICallState.instance.selfUser.value.callStatus.value = TUICallStatus.accept
if callMediaType == .audio {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.earpiece
TUICallState.instance.isCameraOpen.value = false
} else if callMediaType == .video {
TUICallState.instance.audioDevice.value = TUIAudioPlaybackDevice.speakerphone
TUICallState.instance.isCameraOpen.value = true
}
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Constants.EVENT_SHOW_TUICALLKIT_VIEWCONTROLLER), object: nil)
succ()
} fail: { code, message in
fail(code, message)
}
}
func hangup() {
TUICallEngine.createInstance().hangup {
} fail: { code, message in
}
}
func accept() {
TUICallEngine.createInstance().accept {
} fail: { code, message in
}
}
func reject() {
TUICallEngine.createInstance().reject {
} fail: { code, message in
}
}
func muteMic() {
if TUICallState.instance.isMicMute.value == true {
TUICallEngine.createInstance().openMicrophone { [weak self] in
guard let self = self else { return }
TUICallState.instance.isMicMute.value = false
self.voipDataSyncHandler.setVoIPMuteForTUICallKitVoIPExtension(false)
self.voipDataSyncHandler.setVoIPMute(false)
} fail: { code , message in
}
} else {
TUICallEngine.createInstance().closeMicrophone()
TUICallState.instance.isMicMute.value = true
voipDataSyncHandler.setVoIPMuteForTUICallKitVoIPExtension(true)
voipDataSyncHandler.setVoIPMute(true)
}
}
func openMicrophone(_ notifyEvent: Bool = true) {
if TUICallState.instance.selfUser.value.callStatus.value != .none {
TUICallEngine.createInstance().openMicrophone { [weak self] in
guard let self = self else { return }
TUICallState.instance.isMicMute.value = false
if (notifyEvent) {
self.voipDataSyncHandler.setVoIPMuteForTUICallKitVoIPExtension(false)
self.voipDataSyncHandler.setVoIPMute(false)
}
} fail: { code , message in
}
}
}
func closeMicrophone(_ notifyEvent: Bool = true) {
TUICallEngine.createInstance().closeMicrophone()
TUICallState.instance.isMicMute.value = true
if (notifyEvent) {
voipDataSyncHandler.setVoIPMuteForTUICallKitVoIPExtension(true)
voipDataSyncHandler.setVoIPMute(true)
}
}
func changeSpeaker() {
if TUICallState.instance.audioDevice.value == TUIAudioPlaybackDevice.speakerphone {
selectAudioPlaybackDevice(device: .earpiece)
} else {
selectAudioPlaybackDevice(device: .speakerphone)
}
}
func selectAudioPlaybackDevice(device: TUIAudioPlaybackDevice) {
TUICallEngine.createInstance().selectAudioPlaybackDevice(device)
TUICallState.instance.audioDevice.value = device
}
func switchCamera() {
if TUICallState.instance.isFrontCamera.value == .front {
TUICallEngine.createInstance().switchCamera(.back)
TUICallState.instance.isFrontCamera.value = .back
} else {
TUICallEngine.createInstance().switchCamera(.front)
TUICallState.instance.isFrontCamera.value = .front
}
}
func closeCamera() {
TUICallEngine.createInstance().closeCamera()
TUICallState.instance.isCameraOpen.value = false
}
func openCamera(videoView: TUIVideoView) {
TUICallEngine.createInstance().openCamera(TUICallState.instance.isFrontCamera.value == .front ? .front : .back, videoView: videoView) {
TUICallState.instance.isCameraOpen.value = true
} fail: { code, message in
}
}
func startRemoteView(user: User, videoView: TUIVideoView){
TUICallEngine.createInstance().startRemoteView(userId: user.id.value, videoView: videoView) { userId in
} onLoading: { userId in
} onError: { userId, code, message in
}
}
func stopRemoteView(user: User) {
TUICallEngine.createInstance().stopRemoteView(userId: user.id.value)
}
func setAudioPlaybackDevice(device: TUIAudioPlaybackDevice) {
TUICallEngine.createInstance().selectAudioPlaybackDevice(device)
}
func switchToAudio() {
TUICallEngine.createInstance().switchCallMediaType(.audio)
}
func inviteUser(userIds: [String]) {
let callParams = TUICallParams()
callParams.offlinePushInfo = OfflinePushInfoConfig.createOfflinePushInfo()
callParams.timeout = TUI_CALLKIT_SIGNALING_MAX_TIME
TUICallEngine.createInstance().inviteUser(userIdList: userIds, params: callParams) { userIds in
User.getUserInfosFromIM(userIDs: userIds) { newRemoteUsers in
for newUser in newRemoteUsers {
newUser.callStatus.value = TUICallStatus.waiting
newUser.callRole.value = TUICallRole.called
TUICallState.instance.remoteUserList.value.append(newUser)
}
}
} fail: { code, message in
}
}
func addObserver(_ observer: TUICallObserver) {
TUICallEngine.createInstance().addObserver(observer)
}
func removeObserver(_ observer: TUICallObserver) {
TUICallEngine.createInstance().removeObserver(observer)
}
func initEngine(sdkAppId: Int32, userId: String, userSig: String, succ: @escaping TUICallSucc, fail: @escaping TUICallFail) {
TUICallEngine.createInstance().`init`(sdkAppId, userId: userId, userSig: userSig) {
succ()
} fail: { code, message in
fail(code, message)
}
}
func setVideoEncoderParams(params: TUIVideoEncoderParams, succ: @escaping TUICallSucc, fail: @escaping TUICallFail) {
TUICallEngine.createInstance().setVideoEncoderParams(params) {
succ()
} fail: { code, message in
fail(code, message)
}
}
func setVideoRenderParams(userId: String, params: TUIVideoRenderParams, succ: @escaping TUICallSucc, fail: @escaping TUICallFail) {
TUICallEngine.createInstance().setVideoRenderParams(userId: userId, params: params) {
succ()
} fail: { code, message in
fail(code, message)
}
}
func getTRTCCloudInstance() -> TRTCCloud {
return TUICallEngine.createInstance().getTRTCCloudInstance()
}
func setFramework() {
var jsonParams: [String: Any]
if TUICore.getService(TUICore_TUIChatService) == nil {
jsonParams = ["api": "setFramework",
"params": ["component": 14,
"language": 3,],]
} else {
jsonParams = ["api": "setFramework",
"params": ["component": 15,
"language": 3,],]
}
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
}
TUICallEngine.createInstance().callExperimentalAPI(jsonObject: paramsString)
}
func setExcludeFromHistoryMessage() {
if TUICore.getService(TUICore_TUIChatService) == nil {
return
}
let jsonParams: [String: Any] = ["api": "setExcludeFromHistoryMessage",
"params": ["excludeFromHistoryMessage": false,],]
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
}
TUICallEngine.createInstance().callExperimentalAPI(jsonObject: paramsString)
}
func setBlurBackground() {
let currentEnable = TUICallState.instance.enableBlurBackground.value
let level = !currentEnable ? 3 : 0
TUICallState.instance.enableBlurBackground.value = !currentEnable
TUICallEngine.createInstance().setBlurBackground(level) { code, message in
TUICallState.instance.enableBlurBackground.value = false
}
}
func reportOnlineLog(_ enableVirtualBackground: Bool) {
let msgDic: [String: Any] = ["enablevirtualbackground": enableVirtualBackground,
"version": TUICALL_VERSION,
"platform": "iOS",
"framework": "native",
"sdk_app_id": TUILogin.getSdkAppID(),]
guard let msgData = try? JSONSerialization.data(withJSONObject: msgDic,
options: JSONSerialization.WritingOptions(rawValue: 0)) else {
return
}
guard let msgString = NSString(data: msgData, encoding: String.Encoding.utf8.rawValue) as? String else {
return
}
let jsonParams: [String: Any] = ["api": "reportOnlineLog",
"params": ["level": 1,
"msg":msgDic,
"more_msg":"TUICallkit"],]
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
}
TUICallEngine.createInstance().getTRTCCloudInstance().callExperimentalAPI(paramsString)
}
func closeVoIP() {
voipDataSyncHandler.closeVoIP()
}
func callBegin() {
voipDataSyncHandler.callBegin()
}
func updateVoIPInfo(callerId: String, calleeList: [String], groupId: String) {
voipDataSyncHandler.updateVoIPInfo(callerId: callerId, calleeList: calleeList, groupId: groupId)
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "audio_call@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "default_user_icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "group_network_low_quality@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "group_network_low_quality@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "group_switch_camera@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "group_switch_camera@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "group_virtual_background_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "group_virtual_background_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "group_virtual_background_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "group_virtual_background_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_calls_edit.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_calls_edit@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_recents_audio@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_recents_audio@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "ic_recents_more.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_recents_video@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_recents_video@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_add_user@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_add_user@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_big_switch_camera@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_big_switch_camera@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_big_virtual_background_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_big_virtual_background_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_big_virtual_background_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_big_virtual_background_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_camera_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_camera_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_camera_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_camera_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tuicallkit_check_box_group_selected.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "tuicallkit_check_box_group_unselected.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_dialing@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_dialing@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_float_dialing@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_float_dialing@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_float_group_audio_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_float_group_audio_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_float_group_audio_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_float_group_audio_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_float_group_video_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_float_group_video_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_float_group_video_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_float_group_video_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_handsfree@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_handsfree@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_handsfree_on@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_handsfree_on@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_hangup@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_hangup@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_join_group@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_join_group@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_join_group_expand@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_join_group_expand@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_join_group_zoom@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_join_group_zoom@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_match@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_match@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_mic_off@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_mic_off@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ic_min_window@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_min_window@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Some files were not shown because too many files have changed in this diff Show More