提交
78
TUIKit/TUICallKit/TUICallKit-Swift/Data/Constants.swift
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
95
TUIKit/TUICallKit/TUICallKit-Swift/Data/User.swift
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/audio_call.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
BIN
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/audio_call.imageset/audio_call@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 611 B |
|
After Width: | Height: | Size: 821 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 453 B |
|
After Width: | Height: | Size: 621 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1004 B |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
22
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_dialing.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 478 B |
|
After Width: | Height: | Size: 620 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 357 B |
|
After Width: | Height: | Size: 417 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 378 B |
|
After Width: | Height: | Size: 458 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 197 B |
|
After Width: | Height: | Size: 256 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 201 B |
|
After Width: | Height: | Size: 270 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
22
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_hangup.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
BIN
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_hangup.imageset/ic_hangup@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_hangup.imageset/ic_hangup@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 885 B |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 621 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 988 B |
|
After Width: | Height: | Size: 1.2 KiB |
22
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_match.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
BIN
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_match.imageset/icon_match@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 640 B |
BIN
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_match.imageset/icon_match@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 841 B |
22
TUIKit/TUICallKit/TUICallKit-Swift/Resources/Assets.xcassets/icon_mic_off.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 811 B |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||