增加换肤功能
This commit is contained in:
20
TUIKit/TUIRoomKit/Source/View/Model/AudioModel.swift
Normal file
20
TUIKit/TUIRoomKit/Source/View/Model/AudioModel.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AudioModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/3/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class AudioModel {
|
||||
var isMicOpened: Bool = false
|
||||
var isSoundOnSpeaker: Bool = true
|
||||
var captureVolume: Int = 100
|
||||
var playVolume: Int = 100
|
||||
var volumePrompt: Bool = true
|
||||
var isRecord: Bool = false
|
||||
var audioQuality: TUIAudioQuality = .default
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// ConferenceListManagerObserver.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class ConferenceListObserver: NSObject, TUIConferenceListManagerObserver {
|
||||
private var roomInfo: TUIRoomInfo {
|
||||
EngineManager.shared.store.roomInfo
|
||||
}
|
||||
|
||||
func onConferenceInfoChanged(conferenceInfo: TUIConferenceInfo, modifyFlag: TUIConferenceModifyFlag) {
|
||||
guard conferenceInfo.basicRoomInfo.roomId == roomInfo.roomId else { return }
|
||||
roomInfo.name = conferenceInfo.basicRoomInfo.name
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onConferenceInfoChanged, param: ["conferenceInfo": conferenceInfo, "modifyFlag": modifyFlag])
|
||||
}
|
||||
|
||||
func onConferenceScheduled(conferenceInfo: TUIConferenceInfo) {
|
||||
|
||||
}
|
||||
|
||||
func onConferenceWillStart(conferenceInfo: TUIConferenceInfo) {
|
||||
|
||||
}
|
||||
|
||||
func onConferenceCancelled(roomId: String, reason: TUIConferenceCancelReason, operateUser: TUIUserInfo) {
|
||||
|
||||
}
|
||||
|
||||
func onScheduleAttendeesChanged(roomId: String, leftUsers: [TUIUserInfo], joinedUsers: [TUIUserInfo]) {
|
||||
|
||||
}
|
||||
|
||||
func onConferenceStatusChanged(roomId: String, status: TUIConferenceStatus) {
|
||||
|
||||
}
|
||||
}
|
||||
24
TUIKit/TUIRoomKit/Source/View/Model/ConferenceParams.swift
Normal file
24
TUIKit/TUIRoomKit/Source/View/Model/ConferenceParams.swift
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// ConferenceParams.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/3/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class ConferenceParams : NSObject {
|
||||
public var isMuteMicrophone = false
|
||||
public var isOpenCamera = false
|
||||
public var isSoundOnSpeaker = true
|
||||
|
||||
public var name: String?
|
||||
public var enableMicrophoneForAllUser = true
|
||||
public var enableCameraForAllUser = true
|
||||
public var enableMessageForAllUser = true
|
||||
public var enableSeatControl = false
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
127
TUIKit/TUIRoomKit/Source/View/Model/ConferenceSessionImp.swift
Normal file
127
TUIKit/TUIRoomKit/Source/View/Model/ConferenceSessionImp.swift
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// ConferenceSessionImp.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by CY zhao on 2024/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import Factory
|
||||
|
||||
class ConferenceSessionImp: NSObject {
|
||||
private(set) var isEnableWaterMark = false;
|
||||
private(set) var waterMarkText = "";
|
||||
|
||||
private var observers = NSHashTable<ConferenceObserver>.weakObjects()
|
||||
|
||||
// MARK: - Public
|
||||
override init() {
|
||||
super.init()
|
||||
subscribeEngine()
|
||||
}
|
||||
|
||||
func addObserver(observer: ConferenceObserver) {
|
||||
guard !observers.contains(observer) else { return }
|
||||
observers.add(observer)
|
||||
}
|
||||
|
||||
func removeObserver(observer: ConferenceObserver) {
|
||||
guard observers.contains(observer) else { return }
|
||||
observers.remove(observer)
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
unsubscribeEngine()
|
||||
observers.removeAllObjects()
|
||||
}
|
||||
|
||||
func enableWaterMark() {
|
||||
self.isEnableWaterMark = true
|
||||
}
|
||||
|
||||
func setWaterMarkText(waterMarkText: String) {
|
||||
self.waterMarkText = waterMarkText
|
||||
}
|
||||
|
||||
func setContactsViewProvider(_ provider: @escaping (ConferenceParticipants) -> ContactViewProtocol) {
|
||||
Container.shared.contactViewController.register { participants in
|
||||
provider(participants)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
unsubscribeEngine()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
private func subscribeEngine() {
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onExitedRoom, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onDestroyedRoom, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onStartedRoom, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onJoinedRoom, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onRoomDismissed, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onKickedOutOfRoom, observer: self)
|
||||
}
|
||||
|
||||
private func unsubscribeEngine() {
|
||||
EngineEventCenter.shared.unsubscribeEngine(event: .onExitedRoom, observer: self)
|
||||
EngineEventCenter.shared.unsubscribeEngine(event: .onDestroyedRoom, observer: self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - callback
|
||||
extension ConferenceSessionImp: RoomEngineEventResponder {
|
||||
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
|
||||
switch name {
|
||||
case .onStartedRoom:
|
||||
guard let roomInfo = param?["roomInfo"] as? TUIRoomInfo else { return }
|
||||
guard let error = param?["error"] as? TUIError else { return }
|
||||
guard let message = param?["mesasge"] as? String else { return }
|
||||
handleRoomStarted(roomInfo: roomInfo, error: error, message: message)
|
||||
case .onJoinedRoom:
|
||||
guard let roomInfo = param?["roomInfo"] as? TUIRoomInfo else { return }
|
||||
guard let error = param?["error"] as? TUIError else { return }
|
||||
guard let message = param?["mesasge"] as? String else { return }
|
||||
handleRoomJoined(roomInfo: roomInfo, error: error, message: message)
|
||||
case .onDestroyedRoom, .onRoomDismissed:
|
||||
guard let roomId = param?["roomId"] as? String else { return }
|
||||
handleRoomFinished(roomId: roomId)
|
||||
case .onExitedRoom, .onKickedOutOfRoom:
|
||||
guard let roomId = param?["roomId"] as? String else { return }
|
||||
handleRoomExited(roomId: roomId)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func handleRoomStarted(roomInfo: TUIRoomInfo, error: TUIError, message: String) {
|
||||
for observer in observers.allObjects {
|
||||
observer.onConferenceStarted?(roomInfo: roomInfo, error: error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleRoomJoined(roomInfo: TUIRoomInfo, error: TUIError, message: String) {
|
||||
for observer in observers.allObjects {
|
||||
observer.onConferenceJoined?(roomInfo: roomInfo, error: error, message: message)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleRoomFinished(roomId: String) {
|
||||
for observer in observers.allObjects {
|
||||
observer.onConferenceFinished?(roomId: roomId)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleRoomExited(roomId: String) {
|
||||
for observer in observers.allObjects {
|
||||
observer.onConferenceExited?(roomId: roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Container {
|
||||
var contactViewController: ParameterFactory<ConferenceParticipants, ContactViewProtocol?> {
|
||||
promised().scope(.unique)
|
||||
}
|
||||
}
|
||||
183
TUIKit/TUIRoomKit/Source/View/Model/EngineEventCenter.swift
Normal file
183
TUIKit/TUIRoomKit/Source/View/Model/EngineEventCenter.swift
Normal file
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// EngineEventCenter.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by aby on 2023/1/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
protocol RoomKitUIEventResponder: NSObject {
|
||||
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable: Any]?)
|
||||
}
|
||||
|
||||
protocol RoomEngineEventResponder: NSObject {
|
||||
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String: Any]?)
|
||||
}
|
||||
|
||||
class TUINotificationAdapter:NSObject ,TUINotificationProtocol {
|
||||
|
||||
weak var responder: RoomKitUIEventResponder?
|
||||
|
||||
init(responder: RoomKitUIEventResponder? = nil) {
|
||||
self.responder = responder
|
||||
}
|
||||
|
||||
func onNotifyEvent(_ key: String, subKey: String, object anObject: Any?, param: [AnyHashable : Any]?) {
|
||||
guard let eventKey = EngineEventCenter.RoomUIEvent(rawValue: subKey) else { return }
|
||||
responder?.onNotifyUIEvent(key: eventKey, Object: anObject, info: param)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
class EngineEventCenter: NSObject {
|
||||
|
||||
// Weak Ref
|
||||
typealias WeakArray<T> = [() -> T?]
|
||||
static let shared = EngineEventCenter()
|
||||
private var engineObserverMap: [RoomEngineEvent: WeakArray<RoomEngineEventResponder>] = [:]
|
||||
private var uiEventObserverMap: [RoomUIEvent: [TUINotificationAdapter]] = [:]
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
enum RoomEngineEvent: String {
|
||||
case onKickedOffLine
|
||||
case onRoomDismissed
|
||||
case onKickedOutOfRoom
|
||||
case onUserVideoStateChanged
|
||||
case onUserAudioStateChanged
|
||||
case onUserVoiceVolumeChanged
|
||||
case onUserScreenCaptureStopped
|
||||
case onRequestReceived
|
||||
case onSendMessageForUserDisableChanged
|
||||
case onRemoteUserEnterRoom
|
||||
case onRemoteUserLeaveRoom
|
||||
case onUserRoleChanged
|
||||
case onSeatListChanged
|
||||
case onAllUserCameraDisableChanged
|
||||
case onAllUserMicrophoneDisableChanged
|
||||
case onKickedOffSeat
|
||||
case onStatistics
|
||||
case onDeletedTakeSeatRequest
|
||||
case onExitedRoom
|
||||
case onDestroyedRoom
|
||||
case onStartedRoom
|
||||
case onJoinedRoom
|
||||
case onConferenceInfoChanged
|
||||
case onInitialSelfUserInfo
|
||||
case onInitialRoomInfo
|
||||
case onGetUserListFinished
|
||||
}
|
||||
|
||||
enum RoomUIEvent: String {
|
||||
case TUIRoomKitService
|
||||
case TUIRoomKitService_RenewUserList
|
||||
case TUIRoomKitService_SomeoneSharing
|
||||
case TUIRoomKitService_RenewSeatList
|
||||
case TUIRoomKitService_UserOnSeatChanged
|
||||
case TUIRoomKitService_ShowRoomMainView
|
||||
case TUIRoomKitService_ShowRoomVideoFloatView
|
||||
case TUIRoomKitService_CurrentUserHasAudioStream
|
||||
case TUIRoomKitService_CurrentUserHasVideoStream
|
||||
case TUIRoomKitService_CurrentUserRoleChanged
|
||||
case TUIRoomKitService_CurrentUserMuteMessage
|
||||
case TUIRoomKitService_RoomOwnerChanged
|
||||
case TUIRoomKitService_ChangeToolBarHiddenState
|
||||
case TUIRoomKitService_SetToolBarDelayHidden
|
||||
case TUIRoomKitService_HiddenChatWindow
|
||||
case TUIRoomKitService_ShowExitRoomView
|
||||
case TUIRoomKitService_RenewVideoSeatView
|
||||
case TUIRoomKitService_DismissConferenceViewController
|
||||
case TUIRoomKitService_ShowFloatChatView
|
||||
}
|
||||
|
||||
func subscribeUIEvent(key: RoomUIEvent, responder: RoomKitUIEventResponder) {
|
||||
let observer = TUINotificationAdapter(responder: responder)
|
||||
if var observerArray = uiEventObserverMap[key] {
|
||||
observerArray.append(observer)
|
||||
uiEventObserverMap[key] = observerArray
|
||||
} else {
|
||||
uiEventObserverMap[key] = [observer]
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
TUICore.registerEvent(RoomUIEvent.TUIRoomKitService.rawValue, subKey: key.rawValue, object: observer)
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribeUIEvent(key: RoomUIEvent, responder: RoomKitUIEventResponder) {
|
||||
guard var observerArray = uiEventObserverMap[key] else { return }
|
||||
observerArray = observerArray.filter({ observer in
|
||||
guard let responderValue = observer.responder else {
|
||||
DispatchQueue.main.async {
|
||||
TUICore.unRegisterEvent(RoomUIEvent.TUIRoomKitService.rawValue, subKey: key.rawValue, object: observer)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if responderValue.isEqual(responder) {
|
||||
DispatchQueue.main.async {
|
||||
TUICore.unRegisterEvent(RoomUIEvent.TUIRoomKitService.rawValue, subKey: key.rawValue, object: observer)
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
if observerArray.count == 0 {
|
||||
uiEventObserverMap.removeValue(forKey: key)
|
||||
} else {
|
||||
uiEventObserverMap[key] = observerArray
|
||||
}
|
||||
}
|
||||
|
||||
func notifyUIEvent(key: RoomUIEvent, param: [AnyHashable : Any]) {
|
||||
DispatchQueue.main.async {
|
||||
TUICore.notifyEvent(RoomUIEvent.TUIRoomKitService.rawValue, subKey: key.rawValue, object: nil, param: param)
|
||||
}
|
||||
}
|
||||
|
||||
func subscribeEngine(event: RoomEngineEvent, observer: RoomEngineEventResponder) {
|
||||
let weakObserver = { [weak observer] in return observer }
|
||||
if var observerArray = engineObserverMap[event] {
|
||||
let listenerObject = observerArray.first { weakObject in
|
||||
guard let object = weakObject() else { return false }
|
||||
return object.isEqual(observer)
|
||||
}
|
||||
guard listenerObject == nil else { return }
|
||||
observerArray.append(weakObserver)
|
||||
engineObserverMap[event] = observerArray
|
||||
} else {
|
||||
engineObserverMap[event] = [weakObserver]
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribeEngine(event: RoomEngineEvent, observer: RoomEngineEventResponder) {
|
||||
guard var observerArray = engineObserverMap[event] else { return }
|
||||
observerArray.removeAll { weakObject in
|
||||
guard let object = weakObject() else { return true }
|
||||
return object.isEqual(observer)
|
||||
}
|
||||
if observerArray.count == 0 {
|
||||
engineObserverMap.removeValue(forKey: event)
|
||||
} else {
|
||||
engineObserverMap[event] = observerArray
|
||||
}
|
||||
}
|
||||
|
||||
func notifyEngineEvent(event: RoomEngineEvent, param: [String : Any]) {
|
||||
guard let observers = engineObserverMap[event] else { return }
|
||||
observers.forEach { responder in
|
||||
responder()?.onEngineEvent(name: event, param: param)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
818
TUIKit/TUIRoomKit/Source/View/Model/EngineManager.swift
Normal file
818
TUIKit/TUIRoomKit/Source/View/Model/EngineManager.swift
Normal file
@@ -0,0 +1,818 @@
|
||||
//
|
||||
// EngineManager.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by WesleyLei on 2022/9/22.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
#if canImport(TXLiteAVSDK_TRTC)
|
||||
import TXLiteAVSDK_TRTC
|
||||
#elseif canImport(TXLiteAVSDK_Professional)
|
||||
import TXLiteAVSDK_Professional
|
||||
#endif
|
||||
|
||||
class EngineManager: NSObject {
|
||||
static private(set) var shared = EngineManager()
|
||||
|
||||
private(set) lazy var store: RoomStore = {
|
||||
let store = RoomStore()
|
||||
return store
|
||||
}()
|
||||
|
||||
private(set) var roomEngine = TUIRoomEngine.sharedInstance()
|
||||
|
||||
private lazy var eventDispatcher: RoomEventDispatcher = {
|
||||
let eventDispatcher = RoomEventDispatcher()
|
||||
return eventDispatcher
|
||||
}()
|
||||
private lazy var observer: TRTCObserver = {
|
||||
let observer = TRTCObserver()
|
||||
return observer
|
||||
}()
|
||||
private lazy var conferenceListObserver: ConferenceListObserver = {
|
||||
return ConferenceListObserver()
|
||||
}()
|
||||
private lazy var conferenceListManager: TUIConferenceListManager? = {
|
||||
guard let listManager = roomEngine.getExtension(extensionType: .conferenceListManager) as? TUIConferenceListManager else { return nil }
|
||||
return listManager
|
||||
}()
|
||||
private let takeSeatTimeOutNumber: Double = 60
|
||||
private let openRemoteDeviceTimeOutNumber: Double = 15
|
||||
private let rootRouter: RoomRouter = RoomRouter.shared
|
||||
private var isLoginEngine: Bool = false
|
||||
private let appGroupString: String = "com.tencent.TUIRoomTXReplayKit-Screen"
|
||||
|
||||
override private init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
|
||||
func setSelfInfo(userName: String, avatarURL: String, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
store.currentUser.userName = userName
|
||||
store.currentUser.avatarUrl = avatarURL
|
||||
TUIRoomEngine.setSelfInfo(userName: userName, avatarUrl: avatarURL) {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
debugPrint("---setSelfInfo,code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
func createRoom(roomInfo: TUIRoomInfo, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomInfo.name = transferConferenceName(conferenceName: roomInfo.name)
|
||||
if !isLoginEngine {
|
||||
self.login(sdkAppId: Int(TUILogin.getSdkAppID()), userId: TUILogin.getUserID() ?? "", userSig: TUILogin.getUserSig() ?? "")
|
||||
{ [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.isLoginEngine = true
|
||||
self.createEngineRoom(roomInfo: roomInfo, onSuccess: onSuccess, onError: onError)
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
} else {
|
||||
createEngineRoom(roomInfo: roomInfo, onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
}
|
||||
|
||||
func enterRoom(roomId: String, options: TUIEnterRoomOptions? = nil, enableAudio: Bool, enableVideo: Bool, isSoundOnSpeaker: Bool,
|
||||
onSuccess: @escaping TUIRoomInfoBlock, onError: @escaping TUIErrorBlock) {
|
||||
store.videoSetting.isCameraOpened = enableVideo
|
||||
store.audioSetting.isSoundOnSpeaker = isSoundOnSpeaker
|
||||
setFramework()
|
||||
if !isLoginEngine {
|
||||
self.login(sdkAppId: Int(TUILogin.getSdkAppID()), userId: TUILogin.getUserID() ?? "", userSig: TUILogin.getUserSig() ?? "")
|
||||
{ [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.isLoginEngine = true
|
||||
self.enterEngineRoom(roomId: roomId, options: options, enableAudio: enableAudio, onSuccess: onSuccess, onError: onError)
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
} else {
|
||||
enterEngineRoom(roomId: roomId, options: options, enableAudio: enableAudio, onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
}
|
||||
|
||||
func exitRoom(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.exitRoom(syncWaiting: false) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.handleExitRoomResult()
|
||||
onSuccess?()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.handleExitRoomResult()
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func destroyRoom(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.destroyRoom { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.handleDestroyRoomResult()
|
||||
onSuccess?()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.handleDestroyRoomResult()
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func destroyEngineManager() {
|
||||
removeEngineObserver()
|
||||
unsubLogoutNotification()
|
||||
store = RoomStore()
|
||||
}
|
||||
|
||||
func muteLocalAudio() {
|
||||
roomEngine.muteLocalAudio()
|
||||
}
|
||||
|
||||
func unmuteLocalAudio(onSuccess:TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.unmuteLocalAudio {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func openLocalMicrophone(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
let actionBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.roomEngine.openLocalMicrophone(self.store.audioSetting.audioQuality) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.store.audioSetting.isMicOpened = true
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
if RoomCommon.checkAuthorMicStatusIsDenied() {
|
||||
actionBlock()
|
||||
} else {
|
||||
RoomCommon.micStateActionWithPopCompletion { granted in
|
||||
if granted {
|
||||
actionBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func closeLocalCamera() {
|
||||
store.videoSetting.isCameraOpened = false
|
||||
roomEngine.closeLocalCamera()
|
||||
}
|
||||
|
||||
func openLocalCamera(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
let actionBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.store.videoSetting.isCameraOpened = true
|
||||
self.roomEngine.openLocalCamera(isFront: self.store.videoSetting.isFrontCamera, quality:
|
||||
self.store.videoSetting.videoQuality) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
if RoomCommon.checkAuthorCamaraStatusIsDenied() {
|
||||
actionBlock()
|
||||
} else {
|
||||
RoomCommon.cameraStateActionWithPopCompletion { granted in
|
||||
if granted {
|
||||
actionBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func switchCamera() {
|
||||
store.videoSetting.isFrontCamera = !store.videoSetting.isFrontCamera
|
||||
roomEngine.getMediaDeviceManager().switchCamera(store.videoSetting.isFrontCamera)
|
||||
}
|
||||
|
||||
func switchMirror() {
|
||||
store.videoSetting.isMirror = !store.videoSetting.isMirror
|
||||
let params = TRTCRenderParams()
|
||||
params.mirrorType = store.videoSetting.isMirror ? .enable : .disable
|
||||
setLocalRenderParams(params: params)
|
||||
}
|
||||
|
||||
func muteAllAudioAction(isMute: Bool, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
store.roomInfo.isMicrophoneDisableForAllUser = isMute
|
||||
roomEngine.disableDeviceForAllUserByAdmin(device: .microphone, isDisable:
|
||||
store.roomInfo.isMicrophoneDisableForAllUser) {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func muteAllVideoAction(isMute: Bool, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
store.roomInfo.isCameraDisableForAllUser = isMute
|
||||
roomEngine.disableDeviceForAllUserByAdmin(device: .camera, isDisable:
|
||||
store.roomInfo.isCameraDisableForAllUser) {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func takeUserOnSeatByAdmin(userId: String, timeout: Double,
|
||||
onAccepted: @escaping TUIRequestAcceptedBlock,
|
||||
onRejected: @escaping TUIRequestRejectedBlock,
|
||||
onCancelled: @escaping TUIRequestCancelledBlock,
|
||||
onTimeout: @escaping TUIRequestTimeoutBlock,
|
||||
onError: @escaping TUIRequestErrorBlock) {
|
||||
roomEngine.takeUserOnSeatByAdmin(-1, userId: userId, timeout: timeout) { requestId, userId in
|
||||
onAccepted(requestId, userId)
|
||||
} onRejected: { requestId, userId, message in
|
||||
onRejected( requestId, userId, message)
|
||||
} onCancelled: { requestId, userId in
|
||||
onCancelled(requestId, userId)
|
||||
} onTimeout: { requestId, userId in
|
||||
onTimeout(requestId, userId)
|
||||
} onError: { requestId, userId, code, message in
|
||||
onError(requestId, userId, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func setAudioRoute(isSoundOnSpeaker: Bool) {
|
||||
store.audioSetting.isSoundOnSpeaker = isSoundOnSpeaker
|
||||
let route: TUIAudioRoute = isSoundOnSpeaker ? .speakerphone : .earpiece
|
||||
roomEngine.getMediaDeviceManager().setAudioRoute(route)
|
||||
}
|
||||
|
||||
func takeSeat(onAccepted: TUIRequestAcceptedBlock? = nil,
|
||||
onRejected: TUIRequestRejectedBlock? = nil,
|
||||
onCancelled: TUIRequestCancelledBlock? = nil,
|
||||
onTimeout: TUIRequestTimeoutBlock? = nil,
|
||||
onError: TUIRequestErrorBlock? = nil) -> TUIRequest {
|
||||
let request = self.roomEngine.takeSeat(-1, timeout: takeSeatTimeOutNumber) { [weak self] requestId, userId in
|
||||
guard let self = self else { return }
|
||||
self.store.currentUser.isOnSeat = true
|
||||
self.store.selfTakeSeatRequestId = nil
|
||||
onAccepted?(requestId, userId)
|
||||
} onRejected: { [weak self] requestId, userId, message in
|
||||
guard let self = self else { return }
|
||||
self.store.selfTakeSeatRequestId = nil
|
||||
onRejected?(requestId, userId, message)
|
||||
} onCancelled: { [weak self] requestId, userId in
|
||||
guard let self = self else { return }
|
||||
self.store.selfTakeSeatRequestId = nil
|
||||
onCancelled?(requestId, userId)
|
||||
} onTimeout: { [weak self] requestId, userId in
|
||||
guard let self = self else { return }
|
||||
self.store.selfTakeSeatRequestId = nil
|
||||
onTimeout?(requestId, userId)
|
||||
} onError: { [weak self] requestId, userId, code, message in
|
||||
guard let self = self else { return }
|
||||
self.store.selfTakeSeatRequestId = nil
|
||||
onError?(requestId, userId, code, message)
|
||||
}
|
||||
store.selfTakeSeatRequestId = request.requestId
|
||||
return request
|
||||
}
|
||||
|
||||
func cancelTakeSeatRequest() {
|
||||
guard let requestId = store.selfTakeSeatRequestId else { return }
|
||||
cancelRequest(requestId)
|
||||
store.selfTakeSeatRequestId = nil
|
||||
}
|
||||
|
||||
func fetchRoomInfo(roomId: String ,onSuccess: TUIRoomInfoBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.fetchRoomInfo(roomId: roomId, roomType: .conference) { [weak self] roomInfo in
|
||||
guard let self = self, let roomInfo = roomInfo else { return }
|
||||
if roomId == self.store.roomInfo.roomId {
|
||||
self.store.initialRoomInfo(roomInfo)
|
||||
}
|
||||
onSuccess?(roomInfo)
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
debugPrint("fetchRoomInfo,code:\(code), message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
func setLocalRenderParams(params: TRTCRenderParams) {
|
||||
roomEngine.getTRTCCloud().setLocalRenderParams(params)
|
||||
}
|
||||
|
||||
func setGSensorMode(mode: TRTCGSensorMode) {
|
||||
roomEngine.getTRTCCloud().setGSensorMode(mode)
|
||||
}
|
||||
|
||||
func setLocalVideoView(streamType: TUIVideoStreamType, view: UIView?) {
|
||||
roomEngine.setLocalVideoView(streamType: streamType, view: view)
|
||||
}
|
||||
|
||||
func changeUserRole(userId: String, role: TUIRole, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomEngine.changeUserRole(userId: userId, role: role, onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
|
||||
func responseRemoteRequest(_ requestId: String, agree: Bool, onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.responseRemoteRequest(requestId, agree: agree) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func getUserInfo(_ userId: String, onSuccess: @escaping TUIUserInfoBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomEngine.getUserInfo(userId, onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
|
||||
func stopScreenCapture() {
|
||||
roomEngine.stopScreenCapture()
|
||||
}
|
||||
|
||||
func setVideoEncoder(videoQuality: TUIVideoQuality? = nil, bitrate: Int? = nil, fps: Int? = nil) {
|
||||
let param = TUIRoomVideoEncoderParams()
|
||||
store.videoSetting.videoQuality = videoQuality ?? store.videoSetting.videoQuality
|
||||
param.videoResolution = store.videoSetting.videoQuality
|
||||
store.videoSetting.videoBitrate = bitrate ?? store.videoSetting.videoBitrate
|
||||
param.bitrate = store.videoSetting.videoBitrate
|
||||
store.videoSetting.videoFps = fps ?? store.videoSetting.videoFps
|
||||
param.fps = store.videoSetting.videoFps
|
||||
roomEngine.updateVideoQualityEx(streamType: .cameraStream, params: param)
|
||||
}
|
||||
|
||||
func cancelRequest(_ requestId: String, onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.cancelRequest(requestId) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func leaveSeat(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.leaveSeat {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func startScreenCapture() {
|
||||
roomEngine.startScreenCapture(appGroup: appGroupString)
|
||||
}
|
||||
|
||||
func stopPlayRemoteVideo(userId: String, streamType: TUIVideoStreamType) {
|
||||
roomEngine.stopPlayRemoteVideo(userId: userId, streamType: streamType)
|
||||
}
|
||||
|
||||
func setRemoteVideoView(userId: String, streamType: TUIVideoStreamType, view: UIView?) {
|
||||
roomEngine.setRemoteVideoView(userId: userId, streamType: streamType, view: view)
|
||||
}
|
||||
|
||||
func startPlayRemoteVideo(userId: String, streamType: TUIVideoStreamType, onSuccess: TUISuccessBlock? = nil,
|
||||
onLoading: TUIPlayOnLoadingBlock? = nil, onError: TUIPlayOnErrorBlock? = nil) {
|
||||
roomEngine.startPlayRemoteVideo(userId: userId, streamType: streamType, onPlaying: { _ in
|
||||
guard let onSuccess = onSuccess else { return }
|
||||
onSuccess()
|
||||
}, onLoading: { userId in
|
||||
guard let onLoading = onLoading else { return }
|
||||
onLoading(userId)
|
||||
}, onError: { userId, code, message in
|
||||
guard let onError = onError else { return }
|
||||
onError(userId, code, message)
|
||||
})
|
||||
}
|
||||
|
||||
func setAudioCaptureVolume(_ captureVolume: Int) {
|
||||
store.audioSetting.captureVolume = captureVolume
|
||||
roomEngine.getTRTCCloud().setAudioCaptureVolume(captureVolume)
|
||||
}
|
||||
|
||||
func setAudioPlayoutVolume(_ playVolume: Int) {
|
||||
store.audioSetting.playVolume = playVolume
|
||||
roomEngine.getTRTCCloud().setAudioPlayoutVolume(playVolume)
|
||||
}
|
||||
|
||||
func enableAudioVolumeEvaluation(isVolumePrompt: Bool) {
|
||||
store.audioSetting.volumePrompt = isVolumePrompt
|
||||
if isVolumePrompt {
|
||||
roomEngine.getTRTCCloud().enableAudioVolumeEvaluation(300, enable_vad: true)
|
||||
} else {
|
||||
roomEngine.getTRTCCloud().enableAudioVolumeEvaluation(0, enable_vad: false)
|
||||
}
|
||||
}
|
||||
|
||||
func closeRemoteDeviceByAdmin(userId: String, device: TUIMediaDevice, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomEngine.closeRemoteDeviceByAdmin(userId: userId, device: device, onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
|
||||
func openRemoteDeviceByAdmin(userId: String, device: TUIMediaDevice,
|
||||
onAccepted: TUIRequestAcceptedBlock? = nil,
|
||||
onRejected: TUIRequestRejectedBlock? = nil,
|
||||
onCancelled: TUIRequestCancelledBlock? = nil,
|
||||
onTimeout: TUIRequestTimeoutBlock? = nil,
|
||||
onError: TUIRequestErrorBlock? = nil) {
|
||||
roomEngine.openRemoteDeviceByAdmin(userId: userId, device: device, timeout: openRemoteDeviceTimeOutNumber, onAccepted: { requestId, userId in
|
||||
onAccepted?(requestId, userId)
|
||||
}, onRejected: { requestId, userId, message in
|
||||
onRejected?(requestId, userId, message)
|
||||
}, onCancelled: { requestId, userId in
|
||||
onCancelled?(requestId, userId)
|
||||
}, onTimeout: { requestId, userId in
|
||||
onTimeout?(requestId, userId)
|
||||
}) { requestId, userId, code, message in
|
||||
onError?(requestId, userId, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func disableSendingMessageByAdmin(userId: String, isDisable: Bool, onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.disableSendingMessageByAdmin(userId: userId, isDisable: isDisable) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func kickUserOffSeatByAdmin(userId: String, onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.kickUserOffSeatByAdmin(-1, userId: userId) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func kickRemoteUserOutOfRoom(userId: String, onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
roomEngine.kickRemoteUserOutOfRoom(userId) {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func initUserList(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
self.getUserList(nextSequence: 0, localUserList: []) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if self.store.roomInfo.isSeatEnabled {
|
||||
self.getSeatList {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
} else {
|
||||
onSuccess?()
|
||||
}
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func updateVideoQuality(quality: TUIVideoQuality) {
|
||||
roomEngine.updateVideoQuality(quality)
|
||||
}
|
||||
|
||||
func enableGravitySensor(enable: Bool) {
|
||||
roomEngine.enableGravitySensor(enable: enable)
|
||||
}
|
||||
|
||||
func setVideoResolutionMode(streamType: TUIVideoStreamType, resolutionMode: TUIResolutionMode) {
|
||||
roomEngine.setVideoResolutionMode(streamType: streamType, resolutionMode: resolutionMode)
|
||||
}
|
||||
|
||||
func changeRaiseHandNoticeState(isShown: Bool) {
|
||||
store.isShownRaiseHandNotice = isShown
|
||||
}
|
||||
|
||||
func setRemoteRenderParams(userId: String, streamType: TRTCVideoStreamType, params: TRTCRenderParams) {
|
||||
roomEngine.getTRTCCloud().setRemoteRenderParams(userId, streamType: streamType, params: params)
|
||||
}
|
||||
|
||||
func updateSeatApplicationList() {
|
||||
roomEngine.getSeatApplicationList { [weak self] list in
|
||||
guard let self = self else { return }
|
||||
self.store.setInviteSeatList(list: list)
|
||||
} onError: { code, message in
|
||||
debugPrint("getSeatApplicationList,code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
extension EngineManager {
|
||||
private func login(sdkAppId: Int, userId: String, userSig: String, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
V2TIMManager.sharedInstance().initSDK(Int32(sdkAppId), config: V2TIMSDKConfig())
|
||||
store.currentUser.userId = userId
|
||||
TUIRoomEngine.login(sdkAppId: sdkAppId, userId: userId, userSig: userSig) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.isLoginEngine = true
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
private func createEngineRoom(roomInfo: TUIRoomInfo, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
guard !store.isEnteredRoom else {
|
||||
if store.roomInfo.roomId == roomInfo.roomId {
|
||||
RoomVideoFloatView.dismiss()
|
||||
onSuccess()
|
||||
} else {
|
||||
onError(.failed, .inAnotherRoomText)
|
||||
}
|
||||
return
|
||||
}
|
||||
self.store.roomInfo = roomInfo
|
||||
self.roomEngine.createRoom(roomInfo) {
|
||||
onSuccess()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.destroyEngineManager()
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
private func transferConferenceName(conferenceName: String?) -> String {
|
||||
if let confName = conferenceName, !confName.isEmpty {
|
||||
return confName
|
||||
}
|
||||
let selfInfo = TUIRoomEngine.getSelfInfo()
|
||||
let name: String = selfInfo.userName.isEmpty ? selfInfo.userId : selfInfo.userName
|
||||
return name + .quickConferenceText
|
||||
}
|
||||
|
||||
private func enterEngineRoom(roomId: String, options: TUIEnterRoomOptions? = nil, enableAudio: Bool, onSuccess: @escaping TUIRoomInfoBlock, onError: @escaping TUIErrorBlock) {
|
||||
guard !store.isEnteredRoom else {
|
||||
if store.roomInfo.roomId == roomId {
|
||||
onSuccess(store.roomInfo)
|
||||
} else {
|
||||
onError(.failed, .inAnotherRoomText)
|
||||
}
|
||||
return
|
||||
}
|
||||
if let options = options {
|
||||
roomEngine.enterRoom(roomId, roomType: .conference, options: options) { [weak self] roomInfo in
|
||||
guard let self = self else { return }
|
||||
guard let roomInfo = roomInfo else { return }
|
||||
self.handleEnterRoomResult(roomInfo: roomInfo, enableAudio: enableAudio, onSuccess: onSuccess, onError: onError)
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.destroyEngineManager()
|
||||
onError(code, message)
|
||||
}
|
||||
} else {
|
||||
roomEngine.enterRoom(roomId) { [weak self] roomInfo in
|
||||
guard let self = self else { return }
|
||||
guard let roomInfo = roomInfo else { return }
|
||||
self.handleEnterRoomResult(roomInfo: roomInfo, enableAudio: enableAudio, onSuccess: onSuccess, onError: onError)
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.destroyEngineManager()
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleEnterRoomResult(roomInfo: TUIRoomInfo, enableAudio: Bool, onSuccess: @escaping TUIRoomInfoBlock, onError: @escaping TUIErrorBlock) {
|
||||
//Update the room entry data stored
|
||||
addEngineObserver()
|
||||
store.initialRoomInfo(roomInfo)
|
||||
store.initialRoomCurrentUser()
|
||||
//Initialize user list
|
||||
initUserList()
|
||||
//Initialize video settings
|
||||
initLocalVideoState()
|
||||
subLogoutNotification()
|
||||
updateSeatApplicationList()
|
||||
if !isNeededAutoTakeSeat() {
|
||||
operateLocalMicrophone(enableAudio: enableAudio)
|
||||
updateCameraState()
|
||||
store.initalEnterRoomMessage()
|
||||
onSuccess(roomInfo)
|
||||
} else {
|
||||
autoTakeSeatForOwner { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.operateLocalMicrophone(enableAudio: enableAudio)
|
||||
self.updateCameraState()
|
||||
self.store.initalEnterRoomMessage()
|
||||
onSuccess(roomInfo)
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.destroyEngineManager()
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateCameraState() {
|
||||
if store.roomInfo.isSeatEnabled && !store.currentUser.isOnSeat {
|
||||
store.videoSetting.isCameraOpened = false
|
||||
return
|
||||
}
|
||||
operateLocalCamera()
|
||||
}
|
||||
|
||||
private func isNeededAutoTakeSeat() -> Bool {
|
||||
return store.roomInfo.isSeatEnabled && store.currentUser.userId == store.roomInfo.ownerId
|
||||
}
|
||||
|
||||
private func autoTakeSeatForOwner(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
_ = self.takeSeat() { _,_ in
|
||||
onSuccess()
|
||||
} onError: { _, _, code, message in
|
||||
if code == .alreadyInSeat {
|
||||
onSuccess()
|
||||
} else {
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func isPushLocalAudioStream(enableAudio: Bool) -> Bool {
|
||||
if !enableAudio {
|
||||
return false
|
||||
}
|
||||
if store.roomInfo.isMicrophoneDisableForAllUser, store.currentUser.userId != store.roomInfo.ownerId {
|
||||
return false
|
||||
}
|
||||
if store.roomInfo.isSeatEnabled, store.currentUser.userId != store.roomInfo.ownerId {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func operateLocalMicrophone(enableAudio: Bool ,onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) {
|
||||
if isPushLocalAudioStream(enableAudio: enableAudio) {
|
||||
openLocalMicrophone()
|
||||
} else if RoomCommon.checkAuthorMicStatusIsDenied() {
|
||||
muteLocalAudio()
|
||||
openLocalMicrophone()
|
||||
}
|
||||
}
|
||||
|
||||
private func operateLocalCamera() {
|
||||
let openLocalCameraActionBlock = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
setLocalVideoView(streamType: .cameraStream, view: nil)
|
||||
openLocalCamera()
|
||||
}
|
||||
if store.videoSetting.isCameraOpened && !store.roomInfo.isCameraDisableForAllUser {
|
||||
if RoomCommon.checkAuthorCamaraStatusIsDenied() {
|
||||
openLocalCameraActionBlock()
|
||||
} else {
|
||||
RoomCommon.cameraStateActionWithPopCompletion { granted in
|
||||
if granted {
|
||||
openLocalCameraActionBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func initLocalVideoState() {
|
||||
setVideoParam()
|
||||
enableGravitySensor(enable: true)
|
||||
setGSensorMode(mode: .uiFixLayout)
|
||||
let resolutionMode: TUIResolutionMode = isLandscape ? .landscape : .portrait
|
||||
setVideoResolutionMode(streamType: .cameraStream, resolutionMode: resolutionMode)
|
||||
}
|
||||
|
||||
private func setVideoParam() {
|
||||
setVideoEncoder(videoQuality: store.videoSetting.videoQuality, bitrate: store.videoSetting.videoBitrate,
|
||||
fps: store.videoSetting.videoFps)
|
||||
let params = TRTCRenderParams()
|
||||
params.fillMode = .fill
|
||||
params.rotation = ._0
|
||||
setLocalRenderParams(params: params)
|
||||
}
|
||||
|
||||
private func getUserList(nextSequence: Int, localUserList: [UserEntity], onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomEngine.getUserList(nextSequence: nextSequence) { [weak self] list, nextSequence in
|
||||
guard let self = self else { return }
|
||||
var localUserList = localUserList
|
||||
list.forEach { userInfo in
|
||||
if userInfo.userName.isEmpty {
|
||||
userInfo.userName = userInfo.userId
|
||||
}
|
||||
let userModel = UserEntity()
|
||||
userModel.update(userInfo: userInfo)
|
||||
localUserList.append(userModel)
|
||||
}
|
||||
if nextSequence != 0 {
|
||||
self.getUserList(nextSequence: nextSequence, localUserList: localUserList, onSuccess: onSuccess, onError: onError)
|
||||
} else {
|
||||
self.store.attendeeList = localUserList
|
||||
onSuccess()
|
||||
if !self.store.roomInfo.isSeatEnabled {
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, param: [:])
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onGetUserListFinished, param: [:])
|
||||
}
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
debugPrint("getUserList:code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
private func getSeatList(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
roomEngine.getSeatList { [weak self] seatList in
|
||||
guard let self = self else { return }
|
||||
self.store.initialSeatList(seatList: seatList)
|
||||
self.store.initialOffSeatList()
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
debugPrint("getSeatList:code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
private func addEngineObserver() {
|
||||
roomEngine.addObserver(eventDispatcher)
|
||||
roomEngine.getTRTCCloud().addDelegate(observer)
|
||||
conferenceListManager?.addObserver(conferenceListObserver)
|
||||
}
|
||||
|
||||
private func removeEngineObserver() {
|
||||
roomEngine.removeObserver(eventDispatcher)
|
||||
roomEngine.getTRTCCloud().removeDelegate(observer)
|
||||
conferenceListManager?.removeObserver(conferenceListObserver)
|
||||
}
|
||||
|
||||
private func subLogoutNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleLogout),
|
||||
name: NSNotification.Name.TUILogoutSuccess, object: nil)
|
||||
}
|
||||
|
||||
private func unsubLogoutNotification() {
|
||||
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.TUILogoutSuccess, object: nil)
|
||||
}
|
||||
|
||||
@objc private func handleLogout() {
|
||||
destroyEngineManager()
|
||||
}
|
||||
|
||||
private func handleDestroyRoomResult() {
|
||||
destroyEngineManager()
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onDestroyedRoom, param: [ "roomId" : store.roomInfo.roomId ])
|
||||
}
|
||||
|
||||
private func handleExitRoomResult() {
|
||||
destroyEngineManager()
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onExitedRoom, param: [ "roomId" : store.roomInfo.roomId ])
|
||||
}
|
||||
|
||||
}
|
||||
// MARK: - TUIExtensionProtocol
|
||||
|
||||
extension EngineManager: TUIExtensionProtocol {
|
||||
func getExtensionInfo(_ key: String, param: [AnyHashable: Any]?) -> [AnyHashable: Any] {
|
||||
guard let param = param else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
guard let roomId: String = param["roomId"] as? String else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
if key == gRoomEngineKey {
|
||||
return [key: roomEngine]
|
||||
} else if key == gRoomInfoKey {
|
||||
return [key: store.roomInfo]
|
||||
} else if key == gLocalUserInfoKey {
|
||||
return [key: store.currentUser]
|
||||
} else {
|
||||
return [:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - setFramework
|
||||
extension EngineManager {
|
||||
fileprivate static let TUIRoomKitFrameworkValue = 1
|
||||
fileprivate static let TUIRoomKitComponentValue = 18
|
||||
fileprivate static let IMComponentValue = 19
|
||||
fileprivate static let TUIRoomKitLanguageValue = 3
|
||||
private func setFramework() {
|
||||
let componentValue = store.isImAccess ? EngineManager.IMComponentValue : EngineManager.TUIRoomKitComponentValue
|
||||
let jsonStr = """
|
||||
{
|
||||
"api":"setFramework",
|
||||
"params":{
|
||||
"framework":\(EngineManager.TUIRoomKitFrameworkValue),
|
||||
"component":\(componentValue),
|
||||
"language":\(EngineManager.TUIRoomKitLanguageValue)
|
||||
}
|
||||
}
|
||||
"""
|
||||
TUIRoomEngine.callExperimentalAPI(jsonStr: jsonStr)
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var inAnotherRoomText: String {
|
||||
localized("You are already in another conference")
|
||||
}
|
||||
static var quickConferenceText: String {
|
||||
localized("'s quick meeting")
|
||||
}
|
||||
}
|
||||
29
TUIKit/TUIRoomKit/Source/View/Model/RequestEntity.swift
Normal file
29
TUIKit/TUIRoomKit/Source/View/Model/RequestEntity.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// RequestEntity.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/3/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RequestEntity {
|
||||
let requestId: String
|
||||
let userId: String
|
||||
var userName: String = ""
|
||||
var avatarUrl: String = ""
|
||||
let timestamp: TimeInterval
|
||||
|
||||
init(requestId: String, userId: String) {
|
||||
timestamp = Date().timeIntervalSince1970
|
||||
self.requestId = requestId
|
||||
self.userId = userId
|
||||
guard let userItem = EngineManager.shared.store.attendeeList.first(where: { $0.userId == userId }) else { return }
|
||||
userName = userItem.userName
|
||||
avatarUrl = userItem.avatarUrl
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
282
TUIKit/TUIRoomKit/Source/View/Model/RoomEventDispatcher.swift
Normal file
282
TUIKit/TUIRoomKit/Source/View/Model/RoomEventDispatcher.swift
Normal file
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// RoomEventDispatcher.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/8/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class RoomEventDispatcher: NSObject {
|
||||
var engineManager: EngineManager {
|
||||
EngineManager.shared
|
||||
}
|
||||
var store: RoomStore {
|
||||
engineManager.store
|
||||
}
|
||||
var roomInfo: TUIRoomInfo {
|
||||
store.roomInfo
|
||||
}
|
||||
var currentUser: UserEntity {
|
||||
store.currentUser
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit")
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomEventDispatcher: TUIRoomObserver {
|
||||
// MARK: - Room event callback
|
||||
func onAllUserMicrophoneDisableChanged(roomId: String, isDisable: Bool) {
|
||||
roomInfo.isMicrophoneDisableForAllUser = isDisable
|
||||
let param = [
|
||||
"roomId" : roomId,
|
||||
"isDisable" : isDisable,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onAllUserMicrophoneDisableChanged, param: param)
|
||||
}
|
||||
|
||||
func onAllUserCameraDisableChanged(roomId: String, isDisable: Bool) {
|
||||
roomInfo.isCameraDisableForAllUser = isDisable
|
||||
let param = [
|
||||
"roomId" : roomId,
|
||||
"isDisable" : isDisable,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onAllUserCameraDisableChanged, param: param)
|
||||
}
|
||||
|
||||
func onSendMessageForAllUserDisableChanged(roomId: String, isDisable: Bool) {
|
||||
roomInfo.isMessageDisableForAllUser = isDisable
|
||||
}
|
||||
|
||||
func onRoomDismissed(roomId: String) {
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onRoomDismissed, param: ["roomId" : roomId,])
|
||||
}
|
||||
|
||||
func onKickedOutOfRoom(roomId: String, reason: TUIKickedOutOfRoomReason, message: String) {
|
||||
let param = [
|
||||
"roomId" : roomId,
|
||||
"reason" : reason,
|
||||
"message" : message,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOutOfRoom, param: param)
|
||||
}
|
||||
|
||||
func onRoomNameChanged(roomId: String, roomName: String) {
|
||||
roomInfo.name = roomName
|
||||
}
|
||||
|
||||
// MARK: - User event callback in the room
|
||||
func onRemoteUserEnterRoom(roomId: String, userInfo: TUIUserInfo) {
|
||||
store.remoteUserEnterRoom(userInfo: userInfo)
|
||||
let param = [
|
||||
"roomId" : roomId,
|
||||
"userInfo" : userInfo,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserEnterRoom, param: param)
|
||||
}
|
||||
|
||||
func onRemoteUserLeaveRoom(roomId: String, userInfo: TUIUserInfo) {
|
||||
store.remoteUserLeaveRoom(userInfo: userInfo)
|
||||
let param = [
|
||||
"roomId" : roomId,
|
||||
"userInfo" : userInfo,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserLeaveRoom, param: param)
|
||||
}
|
||||
|
||||
func onUserRoleChanged(userId: String, userRole: TUIRole) {
|
||||
userRoleChanged(userId: userId, userRole: userRole)
|
||||
let param = [
|
||||
"userId" : userId,
|
||||
"userRole" : userRole,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onUserRoleChanged, param: param)
|
||||
}
|
||||
|
||||
func onUserVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) {
|
||||
userVideoStateChanged(userId: userId, streamType: streamType, hasVideo: hasVideo, reason: reason)
|
||||
let param = [
|
||||
"userId" : userId,
|
||||
"streamType" : streamType,
|
||||
"hasVideo" : hasVideo,
|
||||
"reason" : reason,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onUserVideoStateChanged, param: param)
|
||||
}
|
||||
|
||||
func onUserAudioStateChanged(userId: String, hasAudio: Bool, reason: TUIChangeReason) {
|
||||
userAudioStateChanged(userId: userId, hasAudio: hasAudio, reason: reason)
|
||||
let param = [
|
||||
"userId" : userId,
|
||||
"hasAudio" : hasAudio,
|
||||
"reason" : reason,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onUserAudioStateChanged, param: param)
|
||||
}
|
||||
|
||||
func onUserVoiceVolumeChanged(volumeMap: [String : NSNumber]) {
|
||||
userVoiceVolumeChanged(volumeMap: volumeMap)
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onUserVoiceVolumeChanged, param: volumeMap)
|
||||
}
|
||||
|
||||
func onUserScreenCaptureStopped(reason: Int) {
|
||||
userScreenCaptureStopped()
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SomeoneSharing, param: ["userId":currentUser.userId, "hasVideo": false])
|
||||
}
|
||||
|
||||
func onSeatListChanged(seatList: [TUISeatInfo], seated seatedList: [TUISeatInfo], left leftList: [TUISeatInfo]) {
|
||||
seatListChanged(seatList: seatList,seated: seatedList, left: leftList)
|
||||
let param = [
|
||||
"seatList": seatList,
|
||||
"seated": seatedList,
|
||||
"left": leftList,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onSeatListChanged, param: param)
|
||||
}
|
||||
|
||||
func OnSendMessageForUserDisableChanged(roomId: String, userId: String, isDisable muted: Bool) {
|
||||
store.updateUserDisableSendingMessage(userId: userId, isDisable: muted)
|
||||
let param = [
|
||||
"roomId": roomId,
|
||||
"userId": userId,
|
||||
"muted": muted,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onSendMessageForUserDisableChanged, param: param)
|
||||
}
|
||||
|
||||
// MARK: - Signaling request related callbacks
|
||||
func onRequestReceived(request: TUIRequest) {
|
||||
requestReceived(request: request)
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onRequestReceived, param: ["request": request,])
|
||||
}
|
||||
|
||||
func onRequestCancelled(requestId: String, userId: String) {
|
||||
store.deleteTakeSeatRequest(requestId: requestId)
|
||||
}
|
||||
|
||||
func onRequestProcessed(requestId: String, userId: String) {
|
||||
store.deleteTakeSeatRequest(requestId: requestId)
|
||||
}
|
||||
|
||||
func onKickedOffSeat(userId: String) {
|
||||
let param = [
|
||||
"userId": userId,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOffSeat, param: param)
|
||||
}
|
||||
|
||||
func onKickedOffLine(message: String) {
|
||||
kickedOffLine()
|
||||
let param = [
|
||||
"message": message,
|
||||
] as [String : Any]
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onKickedOffLine, param: param)
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomEventDispatcher {
|
||||
private func seatListChanged(seatList: [TUISeatInfo], seated: [TUISeatInfo], left leftList: [TUISeatInfo]) {
|
||||
store.updateLeftSeatList(leftList: leftList)
|
||||
store.updateSeatedList(seatList: seated)
|
||||
}
|
||||
|
||||
private func userAudioStateChanged(userId: String, hasAudio: Bool, reason: TUIChangeReason) {
|
||||
if userId == currentUser.userId {
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserHasAudioStream, param: ["hasAudio": hasAudio, "reason": reason])
|
||||
currentUser.hasAudioStream = hasAudio
|
||||
}
|
||||
guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
|
||||
userModel.hasAudioStream = hasAudio
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
|
||||
}
|
||||
|
||||
private func userVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) {
|
||||
switch streamType {
|
||||
case .screenStream:
|
||||
if userId == currentUser.userId {
|
||||
currentUser.hasScreenStream = hasVideo
|
||||
}
|
||||
guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
|
||||
userModel.hasScreenStream = hasVideo
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SomeoneSharing, param: ["userId": userId, "hasVideo": hasVideo])
|
||||
case .cameraStream:
|
||||
if userId == currentUser.userId {
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream,
|
||||
param: ["hasVideo": hasVideo, "reason": reason])
|
||||
currentUser.hasVideoStream = hasVideo
|
||||
store.videoSetting.isCameraOpened = hasVideo
|
||||
}
|
||||
guard let userModel = store.attendeeList.first(where: { $0.userId == userId }) else { return }
|
||||
userModel.hasVideoStream = hasVideo
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
private func userRoleChanged(userId: String, userRole: TUIRole) {
|
||||
let isSelfRoleChanged = userId == currentUser.userId
|
||||
let isRoomOwnerChanged = userRole == .roomOwner
|
||||
if let userInfo = store.attendeeList.first(where: { $0.userId == userId }) {
|
||||
userInfo.userRole = userRole
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: ["userRole": userRole])
|
||||
}
|
||||
if isSelfRoleChanged {
|
||||
store.currentUser.userRole = userRole
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, param: ["userRole": userRole])
|
||||
}
|
||||
if isRoomOwnerChanged {
|
||||
EngineManager.shared.fetchRoomInfo(roomId: roomInfo.roomId) { _ in
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, param: ["owner": userId])
|
||||
}
|
||||
}
|
||||
if checkAutoTakeSeatForOwner(userId: userId, userRole: userRole) {
|
||||
let _ = engineManager.takeSeat()
|
||||
}
|
||||
if checkAutoSendingMessageForOwner(userId: userId, userRole: userRole) {
|
||||
engineManager.disableSendingMessageByAdmin(userId: userId, isDisable: false)
|
||||
}
|
||||
if isSelfRoleChanged, userRole != .generalUser {
|
||||
engineManager.updateSeatApplicationList()
|
||||
}
|
||||
}
|
||||
|
||||
private func checkAutoTakeSeatForOwner(userId: String, userRole: TUIRole) -> Bool {
|
||||
let isSelfRoleChanged = userId == currentUser.userId
|
||||
let isRoomOwnerChanged = userRole == .roomOwner
|
||||
return isSelfRoleChanged && isRoomOwnerChanged && roomInfo.isSeatEnabled && !currentUser.isOnSeat
|
||||
}
|
||||
|
||||
private func checkAutoSendingMessageForOwner(userId: String, userRole: TUIRole) -> Bool {
|
||||
let isSelfRoleChanged = userId == currentUser.userId
|
||||
let isRoomOwnerChanged = userRole == .roomOwner
|
||||
return isSelfRoleChanged && isRoomOwnerChanged && currentUser.disableSendingMessage
|
||||
}
|
||||
|
||||
private func requestReceived(request: TUIRequest) {
|
||||
switch request.requestAction {
|
||||
case .takeSeat:
|
||||
store.addInviteSeatUser(request: request)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
private func userVoiceVolumeChanged(volumeMap: [String : NSNumber]) {
|
||||
for (userId, volume) in volumeMap {
|
||||
guard let userModel = store.attendeeList.first(where: { $0.userId == userId}) else { continue }
|
||||
userModel.userVoiceVolume = volume.intValue
|
||||
}
|
||||
}
|
||||
|
||||
private func userScreenCaptureStopped() {
|
||||
currentUser.hasScreenStream = false
|
||||
guard let userModel = store.attendeeList.first(where: { $0.userId == currentUser.userId }) else { return }
|
||||
userModel.hasScreenStream = false
|
||||
}
|
||||
|
||||
private func kickedOffLine() {
|
||||
engineManager.destroyEngineManager()
|
||||
}
|
||||
}
|
||||
268
TUIKit/TUIRoomKit/Source/View/Model/RoomStore.swift
Normal file
268
TUIKit/TUIRoomKit/Source/View/Model/RoomStore.swift
Normal file
@@ -0,0 +1,268 @@
|
||||
//
|
||||
// RoomStore.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by aby on 2022/12/28.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
import Factory
|
||||
|
||||
let roomHashNumber: Int = 0x3B9AC9FF
|
||||
|
||||
class RoomStore: NSObject {
|
||||
var currentUser: UserEntity = UserEntity()
|
||||
var roomInfo: TUIRoomInfo = TUIRoomInfo()
|
||||
var videoSetting: VideoModel = VideoModel()
|
||||
var audioSetting: AudioModel = AudioModel()
|
||||
var attendeeList: [UserEntity] = [] // User list
|
||||
var seatList: [UserEntity] = [] // List of users who have taken the stage
|
||||
var offSeatList: [UserEntity] = [] // List of users who not on stage
|
||||
var inviteSeatList: [RequestEntity] = [] // List of users who apply to be on stage
|
||||
var isEnteredRoom: Bool = false
|
||||
var timeStampOnEnterRoom: Int = 0 // Timestamp of entering the meeting
|
||||
var isImAccess: Bool = false // Whether TUIRoomKit is entered by IM
|
||||
var selfTakeSeatRequestId: String? // Self ID for applying on stage
|
||||
var shouldShowFloatChatView = true
|
||||
private let openCameraKey = "isOpenCamera"
|
||||
private let openMicrophoneKey = "isOpenMicrophone"
|
||||
private let shownRaiseHandNoticeKey = "isShownRaiseHandNotice"
|
||||
weak var conferenceObserver: ConferenceObserver?
|
||||
|
||||
var isOpenMicrophone: Bool {
|
||||
didSet {
|
||||
UserDefaults.standard.set(isOpenMicrophone, forKey: openMicrophoneKey)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
var isOpenCamera: Bool {
|
||||
didSet {
|
||||
UserDefaults.standard.set(isOpenCamera, forKey: openCameraKey)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
var isShownRaiseHandNotice: Bool {
|
||||
didSet {
|
||||
UserDefaults.standard.set(isShownRaiseHandNotice, forKey: shownRaiseHandNoticeKey)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
if let isOpenMicrophoneValue = UserDefaults.standard.object(forKey: openMicrophoneKey) as? Bool {
|
||||
isOpenMicrophone = isOpenMicrophoneValue
|
||||
} else {
|
||||
isOpenMicrophone = true
|
||||
}
|
||||
if let isOpenCameraValue = UserDefaults.standard.object(forKey: openCameraKey) as? Bool {
|
||||
isOpenCamera = isOpenCameraValue
|
||||
} else {
|
||||
isOpenCamera = true
|
||||
}
|
||||
if let isShownRaiseHandNoticeValue = UserDefaults.standard.object(forKey: shownRaiseHandNoticeKey) as? Bool {
|
||||
isShownRaiseHandNotice = isShownRaiseHandNoticeValue
|
||||
} else {
|
||||
isShownRaiseHandNotice = true
|
||||
}
|
||||
}
|
||||
|
||||
func initialRoomInfo(_ roomInfo: TUIRoomInfo) {
|
||||
self.roomInfo = roomInfo
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onInitialRoomInfo, param: ["roomInfo": roomInfo])
|
||||
}
|
||||
|
||||
func initalEnterRoomMessage() {
|
||||
isEnteredRoom = true
|
||||
timeStampOnEnterRoom = Int(Date().timeIntervalSince1970)
|
||||
}
|
||||
|
||||
func initialRoomCurrentUser() {
|
||||
currentUser.userId = TUILogin.getUserID() ?? ""
|
||||
currentUser.userName = TUILogin.getNickName() ?? ""
|
||||
EngineManager.shared.getUserInfo(currentUser.userId) { [weak self] userInfo in
|
||||
guard let self = self else { return }
|
||||
guard let userInfo = userInfo else { return }
|
||||
self.currentUser.update(userInfo: userInfo)
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onInitialSelfUserInfo, param: [:])
|
||||
} onError: { code, message in
|
||||
debugPrint("getUserInfo,code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
func initialSeatList(seatList: [TUISeatInfo]) {
|
||||
var localSeatList = [UserEntity]()
|
||||
for seatInfo in seatList {
|
||||
guard let userId = seatInfo.userId, userId != "" else { continue }
|
||||
guard let userModel = getUserItem(userId) else { continue }
|
||||
userModel.isOnSeat = true
|
||||
localSeatList.append(userModel)
|
||||
}
|
||||
self.seatList = localSeatList
|
||||
if seatList.contains(where: { $0.userId == currentUser.userId }) {
|
||||
updateSelfOnSeatState(isOnSeat: true)
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, param: [:])
|
||||
}
|
||||
|
||||
func initialOffSeatList() {
|
||||
offSeatList = attendeeList.filter({ !$0.isOnSeat })
|
||||
}
|
||||
|
||||
func updateSelfOnSeatState(isOnSeat: Bool) {
|
||||
currentUser.isOnSeat = isOnSeat
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_UserOnSeatChanged, param: ["isOnSeat": isOnSeat])
|
||||
}
|
||||
|
||||
func updateUserDisableSendingMessage(userId: String, isDisable: Bool) {
|
||||
if userId == currentUser.userId {
|
||||
currentUser.disableSendingMessage = isDisable
|
||||
}
|
||||
guard let userItem = getUserItem(userId) else { return }
|
||||
userItem.disableSendingMessage = isDisable
|
||||
}
|
||||
|
||||
func deleteTakeSeatRequest(requestId: String) {
|
||||
let requestUserId = inviteSeatList.first(where: { $0.requestId == requestId })?.userId
|
||||
inviteSeatList = inviteSeatList.filter { requestItem in
|
||||
requestItem.requestId != requestId
|
||||
}
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onDeletedTakeSeatRequest, param: ["userId": requestUserId ?? ""])
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func deleteInviteSeatUser(_ userId: String) {
|
||||
inviteSeatList = inviteSeatList.filter { requestItem in
|
||||
requestItem.userId != userId
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func addInviteSeatUser(request: TUIRequest) {
|
||||
guard !inviteSeatList.contains(where: { $0.userId == request.userId }) else { return }
|
||||
let requestEntity = RequestEntity(requestId: request.requestId, userId: request.userId)
|
||||
inviteSeatList.append(requestEntity)
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func setCameraOpened(_ isCameraOpened: Bool) {
|
||||
videoSetting.isCameraOpened = isCameraOpened
|
||||
}
|
||||
|
||||
func setSoundOnSpeaker(_ isSoundOnSpeaker: Bool) {
|
||||
audioSetting.isSoundOnSpeaker = isSoundOnSpeaker
|
||||
}
|
||||
|
||||
func setConferenceObserver(_ observer: ConferenceObserver?) {
|
||||
conferenceObserver = observer
|
||||
}
|
||||
|
||||
func setInviteSeatList(list: [TUIRequest]) {
|
||||
inviteSeatList = []
|
||||
for request in list {
|
||||
let requestEntity = RequestEntity(requestId: request.requestId, userId: request.userId)
|
||||
inviteSeatList.append(requestEntity)
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func updateLeftSeatList(leftList: [TUISeatInfo]) {
|
||||
guard leftList.count > 0 else { return }
|
||||
if leftList.contains(where: { $0.userId == currentUser.userId }) {
|
||||
currentUser.isOnSeat = false
|
||||
audioSetting.isMicOpened = false
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_UserOnSeatChanged,
|
||||
param: ["isOnSeat":false])
|
||||
}
|
||||
for seatInfo: TUISeatInfo in leftList {
|
||||
guard let userId = seatInfo.userId else { continue }
|
||||
if let userItem = attendeeList.first(where: { $0.userId == userId }) {
|
||||
userItem.isOnSeat = false
|
||||
addOffSeatItem(userItem)
|
||||
}
|
||||
deleteOnSeatItem(userId)
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func updateSeatedList(seatList: [TUISeatInfo]) {
|
||||
guard seatList.count > 0 else { return }
|
||||
if seatList.contains(where: { $0.userId == currentUser.userId }) {
|
||||
currentUser.isOnSeat = true
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_UserOnSeatChanged,
|
||||
param: ["isOnSeat":true])
|
||||
}
|
||||
for seatInfo: TUISeatInfo in seatList {
|
||||
guard let userId = seatInfo.userId else { continue }
|
||||
guard let userInfo = attendeeList.first(where: { $0.userId == userId }) else { continue }
|
||||
userInfo.isOnSeat = true
|
||||
addOnSeatItem(userInfo)
|
||||
deleteOffSeatItem(userId)
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:])
|
||||
}
|
||||
|
||||
func remoteUserEnterRoom(userInfo: TUIUserInfo) {
|
||||
let userItem = UserEntity()
|
||||
userItem.update(userInfo: userInfo)
|
||||
addUserItem(userItem)
|
||||
if roomInfo.isSeatEnabled, !userItem.isOnSeat {
|
||||
addOffSeatItem(userItem)
|
||||
}
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
|
||||
}
|
||||
|
||||
func remoteUserLeaveRoom(userInfo: TUIUserInfo) {
|
||||
deleteUserItem(userInfo.userId)
|
||||
deleteOffSeatItem(userInfo.userId)
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:])
|
||||
}
|
||||
|
||||
func updateFloatChatShowState(shouldShow: Bool) {
|
||||
shouldShowFloatChatView = shouldShow
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowFloatChatView, param: ["shouldShow": shouldShow])
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("self:\(self),deinit")
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomStore {
|
||||
private func getUserItem(_ userId: String) -> UserEntity? {
|
||||
return attendeeList.first(where: {$0.userId == userId})
|
||||
}
|
||||
|
||||
private func addUserItem(_ userItem: UserEntity) {
|
||||
guard getUserItem(userItem.userId) == nil else { return }
|
||||
if userItem.userName.isEmpty {
|
||||
userItem.userName = userItem.userId
|
||||
}
|
||||
attendeeList.append(userItem)
|
||||
}
|
||||
|
||||
private func deleteUserItem(_ userId: String) {
|
||||
attendeeList.removeAll(where: { $0.userId == userId })
|
||||
}
|
||||
|
||||
private func addOnSeatItem(_ userItem: UserEntity) {
|
||||
guard !seatList.contains(where: { $0.userId == userItem.userId }) else { return }
|
||||
seatList.append(userItem)
|
||||
}
|
||||
|
||||
private func deleteOnSeatItem(_ userId: String) {
|
||||
seatList.removeAll(where: { $0.userId == userId })
|
||||
}
|
||||
|
||||
private func addOffSeatItem(_ userItem: UserEntity) {
|
||||
guard !offSeatList.contains(where: { $0.userId == userItem.userId }) else { return }
|
||||
offSeatList.append(userItem)
|
||||
}
|
||||
|
||||
private func deleteOffSeatItem(_ userId: String) {
|
||||
offSeatList.removeAll(where: { $0.userId == userId })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// ButtonItemData.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/1/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ButtonItemData {
|
||||
enum ButtonType {
|
||||
case muteAudioItemType
|
||||
case muteVideoItemType
|
||||
case raiseHandItemType
|
||||
case leaveSeatItemType
|
||||
case shareScreenItemType
|
||||
case moreItemType
|
||||
case switchCamaraItemType
|
||||
case raiseHandApplyItemType
|
||||
case normal
|
||||
}
|
||||
enum Orientation {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
var buttonType: ButtonType = .normal
|
||||
var normalIcon: String = ""
|
||||
var selectedIcon: String = ""
|
||||
var disabledIcon: String = ""
|
||||
|
||||
var normalTitle: String = ""
|
||||
var selectedTitle: String = ""
|
||||
|
||||
var titleFont: UIFont?
|
||||
var titleColor: UIColor?
|
||||
|
||||
var resourceBundle: Bundle = Bundle.main
|
||||
|
||||
var action: ((Any)->Void)?
|
||||
|
||||
var normalImage: UIImage? {
|
||||
return UIImage(named: normalIcon, in: resourceBundle, compatibleWith: nil)?.checkOverturn()
|
||||
}
|
||||
|
||||
var selectedImage: UIImage? {
|
||||
return UIImage(named: selectedIcon, in: resourceBundle, compatibleWith: nil)?.checkOverturn()
|
||||
}
|
||||
|
||||
var disabledImage: UIImage? {
|
||||
return UIImage(named: disabledIcon, in: resourceBundle, compatibleWith: nil)?.checkOverturn()
|
||||
}
|
||||
|
||||
var hasNotice: Bool = false
|
||||
var noticeText: String = ""
|
||||
|
||||
var cornerRadius: CGFloat?
|
||||
var hasLineView: Bool = false
|
||||
var orientation: Orientation = .left
|
||||
var imageSize: CGSize?
|
||||
var size: CGSize?
|
||||
var backgroundColor: UIColor?
|
||||
var isSelect: Bool = false
|
||||
var isEnabled: Bool = true
|
||||
var isHidden: Bool = false
|
||||
var alpha: CGFloat = 1
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// ListCellItemData.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/1/6.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ListCellItemData {
|
||||
enum ListCellType {
|
||||
case resolutionType
|
||||
case frameRateType
|
||||
case normal
|
||||
}
|
||||
var type: ListCellType = .normal
|
||||
var size: CGSize?
|
||||
var backgroundColor: UIColor = .clear
|
||||
//UILabel configuration
|
||||
var titleText: String = ""
|
||||
var messageText: String = ""
|
||||
var titleColor: UIColor?
|
||||
var messageColor: UIColor?
|
||||
//UISwitch configuration
|
||||
var hasSwitch: Bool = false
|
||||
var isSwitchOn: Bool = false
|
||||
//UIButton configuration
|
||||
var hasRightButton: Bool = false
|
||||
var buttonData: ButtonItemData?
|
||||
//UISlider configuration
|
||||
var hasSliderLabel: Bool = false
|
||||
var hasSlider: Bool = false
|
||||
var minimumValue: Float = 0
|
||||
var maximumValue: Float = 100
|
||||
var sliderStep: Float = 1
|
||||
var sliderUnit: String = ""
|
||||
var sliderDefault: Float = 0
|
||||
var action: ((Any)->Void)?
|
||||
var hasOverAllAction: Bool = false
|
||||
var hasDownLineView: Bool = false
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// PrepareSettingItemData.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/1/6.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class PrepareSettingItemData {
|
||||
|
||||
var titleText: String = ""
|
||||
var messageText: String = ""
|
||||
var fieldPlaceholderText: String = ""
|
||||
var fieldText: String = ""
|
||||
var fieldEnable: Bool = false
|
||||
var isSwitchOn: Bool = false
|
||||
var action: ((Any)->Void)?
|
||||
var normalIcon: String = ""
|
||||
var resourceBundle: Bundle = Bundle.main
|
||||
var hasSwitch: Bool = false
|
||||
var hasButton: Bool = false
|
||||
var hasFieldView: Bool = false
|
||||
var hasOverAllAction: Bool = false
|
||||
var size: CGSize?
|
||||
var backgroundColor: UIColor?
|
||||
var hasDownLineView: Bool = true
|
||||
}
|
||||
27
TUIKit/TUIRoomKit/Source/View/Model/TRTCObserver.swift
Normal file
27
TUIKit/TUIRoomKit/Source/View/Model/TRTCObserver.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// TRTCObserver.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/4/1.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
#if canImport(TXLiteAVSDK_TRTC)
|
||||
import TXLiteAVSDK_TRTC
|
||||
#elseif canImport(TXLiteAVSDK_Professional)
|
||||
import TXLiteAVSDK_Professional
|
||||
#endif
|
||||
|
||||
class TRTCObserver: NSObject, TRTCCloudDelegate {
|
||||
var roomId: String {
|
||||
EngineManager.shared.store.roomInfo.roomId
|
||||
}
|
||||
func onExitRoom(_ reason: Int) {
|
||||
guard reason == 2 else { return }
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onRoomDismissed, param: ["roomId": roomId])
|
||||
}
|
||||
|
||||
func onStatistics(_ statistics: TRTCStatistics) {
|
||||
EngineEventCenter.shared.notifyEngineEvent(event: .onStatistics, param: ["statistics": statistics])
|
||||
}
|
||||
}
|
||||
45
TUIKit/TUIRoomKit/Source/View/Model/UserEntity.swift
Normal file
45
TUIKit/TUIRoomKit/Source/View/Model/UserEntity.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// UserEntity.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by WesleyLei on 2022/9/26.
|
||||
// Copyright © 2022 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class UserEntity {
|
||||
var userId: String = ""
|
||||
var userName: String = ""
|
||||
var avatarUrl: String = ""
|
||||
var userRole: TUIRole = .generalUser
|
||||
var userVoiceVolume: Int = 0
|
||||
var hasAudioStream: Bool = false
|
||||
var hasVideoStream: Bool = false
|
||||
var videoStreamType: TUIVideoStreamType = .cameraStream
|
||||
var isOnSeat: Bool = false
|
||||
var disableSendingMessage: Bool = false
|
||||
var hasScreenStream: Bool = false
|
||||
func update(userInfo: TUIUserInfo) {
|
||||
userId = userInfo.userId
|
||||
userName = userInfo.userName
|
||||
avatarUrl = userInfo.avatarUrl
|
||||
userRole = userInfo.userRole
|
||||
hasAudioStream = userInfo.hasAudioStream
|
||||
hasVideoStream = userInfo.hasVideoStream
|
||||
hasScreenStream = userInfo.hasScreenStream
|
||||
}
|
||||
|
||||
init(){}
|
||||
|
||||
init(invitation: TUIInvitation) {
|
||||
self.userId = invitation.invitee.userId
|
||||
self.userName = invitation.invitee.userName
|
||||
self.avatarUrl = invitation.invitee.avatarUrl
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
19
TUIKit/TUIRoomKit/Source/View/Model/VideoModel.swift
Normal file
19
TUIKit/TUIRoomKit/Source/View/Model/VideoModel.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// VideoModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/3/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class VideoModel {
|
||||
var isCameraOpened: Bool = true
|
||||
var videoFps: Int = 15
|
||||
var videoBitrate: Int = 1_200
|
||||
var isMirror: Bool = true
|
||||
var isFrontCamera: Bool = true
|
||||
var videoQuality: TUIVideoQuality = .quality720P
|
||||
}
|
||||
38
TUIKit/TUIRoomKit/Source/View/Model/VideoSeatItem.swift
Normal file
38
TUIKit/TUIRoomKit/Source/View/Model/VideoSeatItem.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// VideoSeatItem.swift
|
||||
// TUIVideoSeat
|
||||
//
|
||||
// Created by jack on 2023/3/6.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
|
||||
class VideoSeatItem: Equatable {
|
||||
static func == (lhs: VideoSeatItem, rhs: VideoSeatItem) -> Bool {
|
||||
let lhsType = lhs.videoStreamType == .screenStream
|
||||
let rhsType = rhs.videoStreamType == .screenStream
|
||||
return (lhs.userId == rhs.userId) && (lhsType == rhsType)
|
||||
}
|
||||
var userId: String = ""
|
||||
var userName: String = ""
|
||||
var avatarUrl: String = ""
|
||||
var userRole: TUIRole = .generalUser
|
||||
var userVoiceVolume: Int = 0
|
||||
var hasAudioStream: Bool = false
|
||||
var hasVideoStream: Bool = false
|
||||
var videoStreamType: TUIVideoStreamType = .cameraStream
|
||||
var isOnSeat: Bool = false
|
||||
var disableSendingMessage: Bool = false
|
||||
var isHasVideoStream: Bool {
|
||||
return hasVideoStream || videoStreamType == .screenStream
|
||||
}
|
||||
func update(userInfo: UserEntity) {
|
||||
userId = userInfo.userId
|
||||
userName = userInfo.userName
|
||||
avatarUrl = userInfo.avatarUrl
|
||||
userRole = userInfo.userRole
|
||||
hasAudioStream = userInfo.hasAudioStream
|
||||
hasVideoStream = userInfo.hasVideoStream
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user