443 lines
19 KiB
Swift
443 lines
19 KiB
Swift
//
|
|
// RoomRouter.swift
|
|
// TUIRoomKit
|
|
//
|
|
// Created by janejntang on 2022/9/30.
|
|
// Copyright © 2022 Tencent. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import TUICore
|
|
import RTCRoomEngine
|
|
|
|
// View routing context
|
|
class RouteContext {
|
|
var rootNavigation: UINavigationController?
|
|
typealias Weak<T> = () -> T?
|
|
var alterControllers: [Weak<UIViewController>] = []
|
|
var popUpViewController: Weak<PopUpViewController>?
|
|
var appearance: AnyObject?
|
|
let navigationDelegate = RoomRouter.RoomNavigationDelegate()
|
|
var currentLandscape: Bool = isLandscape
|
|
weak var rootViewController: UIViewController?
|
|
var chatWindow : UIWindow?
|
|
let chatWindowWidth = min(kScreenWidth, kScreenHeight) + 20
|
|
let chatWindowHeight = min(kScreenWidth, kScreenHeight)
|
|
init() {
|
|
if #available(iOS 13, *) {
|
|
appearance = UINavigationBarAppearance()
|
|
}
|
|
}
|
|
}
|
|
|
|
class RoomRouter: NSObject {
|
|
static let shared = RoomRouter()
|
|
private let context: RouteContext = RouteContext()
|
|
private override init() {
|
|
super.init()
|
|
subscribeUIEvent()
|
|
}
|
|
|
|
class RoomNavigationDelegate: NSObject {
|
|
|
|
}
|
|
|
|
var navController: UINavigationController? {
|
|
return context.rootNavigation
|
|
}
|
|
|
|
private func subscribeUIEvent() {
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_ShowRoomMainView, responder: self)
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_ShowRoomVideoFloatView, responder: self)
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_HiddenChatWindow, responder: self)
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_DismissConferenceViewController, responder: self)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(handleDeviceOrientationChange), name: UIDevice.orientationDidChangeNotification, object: nil)
|
|
}
|
|
|
|
private func unsubscribeEvent() {
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_ShowRoomMainView, responder: self)
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_ShowRoomVideoFloatView, responder: self)
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_HiddenChatWindow, responder: self)
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_DismissConferenceViewController, responder: self)
|
|
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
|
|
}
|
|
|
|
func hasChatWindow() -> Bool {
|
|
return context.chatWindow != nil
|
|
}
|
|
|
|
func pushToChatController(user: UserEntity, roomInfo: TUIRoomInfo) {
|
|
guard let chatVC = makeChatController(user: user, roomInfo: roomInfo) else { return }
|
|
if !isLandscape {
|
|
push(viewController: chatVC, animated: false)
|
|
} else {
|
|
let nav = UINavigationController(rootViewController: chatVC)
|
|
nav.navigationBar.backgroundColor = .white
|
|
if #available(iOS 13, *) {
|
|
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return }
|
|
context.chatWindow = UIWindow(windowScene: windowScene)
|
|
} else {
|
|
context.chatWindow = UIWindow(frame: UIScreen.main.bounds)
|
|
}
|
|
context.chatWindow?.frame = CGRect(x: kScreenWidth - context.chatWindowWidth - kDeviceSafeBottomHeight, y: 0, width: context.chatWindowWidth, height: context.chatWindowHeight)
|
|
context.chatWindow?.rootViewController = nav
|
|
context.chatWindow?.windowLevel = UIWindow.Level.statusBar + 1
|
|
context.chatWindow?.isHidden = false
|
|
context.chatWindow?.makeKeyAndVisible()
|
|
}
|
|
}
|
|
|
|
func makeChatController(user: UserEntity, roomInfo: TUIRoomInfo) -> UIViewController? {
|
|
let config: [String : Any] = [
|
|
TUICore_TUIChatService_SetChatExtensionMethod_EnableVideoCallKey: false,
|
|
TUICore_TUIChatService_SetChatExtensionMethod_EnableAudioCallKey: false,
|
|
TUICore_TUIChatService_SetChatExtensionMethod_EnableLinkKey: false,
|
|
]
|
|
TUICore.callService(TUICore_TUIChatService, method: TUICore_TUIChatService_SetChatExtensionMethod, param: config)
|
|
let maxSizeKey = "TUICore_TUIChatService_SetMaxTextSize"
|
|
let chatWidth = min(kScreenWidth, kScreenHeight)
|
|
let sizeParam : [String : Any] = ["maxsize": CGSize(width: chatWidth - 150, height: Double(MAXFLOAT))]
|
|
TUICore.callService(TUICore_TUIChatService, method: maxSizeKey, param: sizeParam)
|
|
let param : [String : Any] = [
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Title : String.chatText,
|
|
TUICore_TUIChatObjectFactory_ChatViewController_GroupID: roomInfo.roomId,
|
|
TUICore_TUIChatObjectFactory_ChatViewController_AvatarUrl : user.avatarUrl,
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_Video_Call : String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_Audio_Call : String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_Room : String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Limit_Portrait_Orientation: String(1),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_Poll : String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_GroupNote : String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_WelcomeCustomMessage :String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_TakePhoto :String(0),
|
|
TUICore_TUIChatObjectFactory_ChatViewController_Enable_RecordVideo :String(0),
|
|
]
|
|
return TUICore.createObject(TUICore_TUIChatObjectFactory, key: TUICore_TUIChatObjectFactory_ChatViewController_Classic,
|
|
param: param) as? UIViewController
|
|
}
|
|
|
|
func pushMainViewController() {
|
|
let vc = makeMainViewController()
|
|
push(viewController: vc)
|
|
}
|
|
|
|
func presentPopUpViewController(viewType: PopUpViewType, height: CGFloat, backgroundColor: UIColor = UIColor(0x1B1E26)) {
|
|
if let observer = context.popUpViewController, let vc = observer() {
|
|
vc.dismiss(animated: false)
|
|
}
|
|
let vc = makePopUpViewController(viewType: viewType, height: height, backgroundColor: backgroundColor)
|
|
let weakObserver = { [weak vc] in return vc }
|
|
context.popUpViewController = weakObserver
|
|
present(viewController: vc)
|
|
}
|
|
|
|
func dismissPopupViewController(completion: (() -> Void)? = nil) {
|
|
guard let observer = context.popUpViewController, let vc = observer() else {
|
|
completion?()
|
|
return
|
|
}
|
|
vc.viewModel.changeSearchControllerActive()
|
|
vc.dismiss(animated: true, completion: completion)
|
|
context.popUpViewController = nil
|
|
}
|
|
|
|
func pop(animated: Bool = true) {
|
|
guard let viewControllerArray = navController?.viewControllers else { return }
|
|
if viewControllerArray.count <= 1 {
|
|
viewControllerArray.first?.dismiss(animated: true)
|
|
navController?.dismiss(animated: true)
|
|
context.rootNavigation = nil
|
|
} else {
|
|
if let vc = viewControllerArray.last, vc is ConferenceMainViewController {
|
|
navController?.popViewController(animated: animated)
|
|
context.rootNavigation = nil
|
|
} else {
|
|
navController?.popViewController(animated: animated)
|
|
}
|
|
}
|
|
}
|
|
|
|
func popToRoomEntranceViewController() {
|
|
if let navController = navController {
|
|
var controllerArray = navController.viewControllers
|
|
controllerArray.reverse()
|
|
for vc in controllerArray {
|
|
if vc is PopUpViewController {
|
|
vc.dismiss(animated: true)
|
|
} else {
|
|
pop()
|
|
}
|
|
if vc is ConferenceMainViewController {
|
|
break
|
|
}
|
|
}
|
|
} else if let vc = context.rootViewController {
|
|
vc.dismiss(animated: true)
|
|
}
|
|
}
|
|
|
|
class func presentAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?, autoConfirmSeconds: Int? = nil) {
|
|
var timer: Timer?
|
|
var remainingSeconds = autoConfirmSeconds ?? 0
|
|
|
|
let alertVC = UIAlertController(title: title,
|
|
message: message,
|
|
preferredStyle: .alert)
|
|
if let declineTitle = declineTitle {
|
|
let declineAction = UIAlertAction(title: declineTitle, style: .destructive) { _ in
|
|
declineBlock?()
|
|
}
|
|
declineAction.setValue(UIColor(0x4F586B), forKey: "titleTextColor")
|
|
alertVC.addAction(declineAction)
|
|
}
|
|
let sureActionTitle = (autoConfirmSeconds != nil) ? "\(sureTitle ?? "") (\(remainingSeconds))" : sureTitle
|
|
let sureAction = UIAlertAction(title: sureActionTitle, style: .default) { _ in
|
|
sureBlock?()
|
|
timer?.invalidate()
|
|
}
|
|
alertVC.addAction(sureAction)
|
|
|
|
if autoConfirmSeconds != nil {
|
|
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
|
|
remainingSeconds -= 1
|
|
if remainingSeconds <= 0 {
|
|
sureBlock?()
|
|
timer?.invalidate()
|
|
} else {
|
|
sureAction.setValue("\(sureTitle ?? "") (\(remainingSeconds))", forKey: "title")
|
|
}
|
|
}
|
|
}
|
|
shared.getCurrentWindowViewController()?.present(alertVC, animated: true)
|
|
let weakObserver = { [weak alertVC] in return alertVC }
|
|
shared.context.alterControllers.append(weakObserver)
|
|
}
|
|
|
|
func dismissAllAlertController(complete: @escaping (()->())) {
|
|
guard context.alterControllers.count > 0 else {
|
|
complete()
|
|
return
|
|
}
|
|
dismissAlertController(index: context.alterControllers.count - 1) { [weak self] in
|
|
guard let self = self else { return }
|
|
self.context.alterControllers = []
|
|
complete()
|
|
}
|
|
}
|
|
|
|
private func dismissAlertController(index: Int, complete: @escaping (()->())) {
|
|
if index < 0 {
|
|
complete()
|
|
return
|
|
}
|
|
if let observer = context.alterControllers[safe: index], let vc = observer() {
|
|
vc.dismiss(animated: true) { [weak self] in
|
|
guard let self = self else { return }
|
|
self.dismissAlertController(index: index - 1, complete: complete)
|
|
}
|
|
} else {
|
|
dismissAlertController(index: index-1, complete: complete)
|
|
}
|
|
}
|
|
|
|
|
|
class func makeToast(toast: String) {
|
|
shared.getCurrentWindowViewController()?.view.makeToast(toast)
|
|
}
|
|
|
|
class func makeToastInCenter(toast: String, duration:TimeInterval) {
|
|
guard let windowView = shared.getCurrentWindowViewController()?.view else {return}
|
|
windowView.makeToast(toast,duration: duration,position:TUICSToastPositionCenter)
|
|
}
|
|
|
|
class func makeToastInWindow(toast: String, duration:TimeInterval) {
|
|
guard let window = RoomRouter.getCurrentWindow() else {return}
|
|
window.makeToast(toast,duration: duration,position:TUICSToastPositionCenter)
|
|
}
|
|
|
|
class func getCurrentWindow() -> UIWindow? {
|
|
var windows: [UIWindow]
|
|
if #available(iOS 13.0, *), let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
windows = windowScene.windows
|
|
} else {
|
|
windows = UIApplication.shared.windows
|
|
}
|
|
if let keyWindow = windows.first(where: { $0.isKeyWindow }) {
|
|
return keyWindow
|
|
} else {
|
|
return windows.last(where: { $0.windowLevel == .normal && $0.isHidden == false &&
|
|
CGRectEqualToRect($0.bounds , UIScreen.main.bounds) })
|
|
}
|
|
}
|
|
|
|
func initializeNavigationController(rootViewController: UIViewController) {
|
|
guard context.rootNavigation == nil else { return }
|
|
if let nav = rootViewController.navigationController {
|
|
context.rootNavigation = nav
|
|
}
|
|
context.rootViewController = rootViewController
|
|
}
|
|
|
|
@objc func handleDeviceOrientationChange() {
|
|
guard context.currentLandscape != isLandscape else { return }
|
|
destroyChatWindow()
|
|
context.currentLandscape = isLandscape
|
|
}
|
|
|
|
deinit {
|
|
unsubscribeEvent()
|
|
debugPrint("deinit \(self)")
|
|
}
|
|
}
|
|
|
|
extension RoomRouter {
|
|
|
|
func push(viewController: UIViewController, animated: Bool = true) {
|
|
guard let navController = navController else {
|
|
createRootNavigationAndPresent(controller: viewController)
|
|
return
|
|
}
|
|
navController.pushViewController(viewController, animated: animated)
|
|
}
|
|
|
|
func present(viewController: UIViewController, animated: Bool = true) {
|
|
if #available(iOS 13.0, *) {
|
|
viewController.modalPresentationStyle = .automatic
|
|
} else {
|
|
viewController.modalPresentationStyle = .overFullScreen
|
|
}
|
|
if let navController = navController {
|
|
navController.present(viewController, animated: animated)
|
|
} else if let vc = context.rootViewController {
|
|
vc.present(viewController, animated: true)
|
|
}
|
|
}
|
|
|
|
private func createRootNavigationAndPresent(controller: UIViewController) {
|
|
let navigationController = RoomKitNavigationController(rootViewController: controller)
|
|
navigationController.modalPresentationStyle = .fullScreen
|
|
context.rootNavigation = navigationController
|
|
if #available(iOS 13.0, *) {
|
|
setupNavigationBarAppearance()
|
|
if let appearance = context.appearance as? UINavigationBarAppearance {
|
|
navigationController.navigationBar.standardAppearance = appearance
|
|
navigationController.navigationBar.scrollEdgeAppearance = appearance
|
|
}
|
|
} else {
|
|
navigationController.navigationBar.shadowImage = UIImage()
|
|
navigationController.navigationBar.barStyle = .default
|
|
}
|
|
let weakObserver = { [weak navigationController] in
|
|
return navigationController
|
|
}
|
|
guard let controller = getCurrentWindowViewController() else { return }
|
|
controller.present(navigationController, animated: true)
|
|
}
|
|
|
|
@available(iOS 13.0, *)
|
|
private func setupNavigationBarAppearance() {
|
|
guard let barAppearance = context.appearance as? UINavigationBarAppearance else {
|
|
return
|
|
}
|
|
barAppearance.configureWithDefaultBackground()
|
|
barAppearance.shadowColor = nil
|
|
barAppearance.backgroundEffect = nil
|
|
barAppearance.backgroundColor = .white
|
|
}
|
|
|
|
private func getCurrentWindowViewController() -> UIViewController? {
|
|
var keyWindow: UIWindow?
|
|
for window in UIApplication.shared.windows {
|
|
if window.isMember(of: UIWindow.self), window.isKeyWindow {
|
|
keyWindow = window
|
|
break
|
|
}
|
|
}
|
|
guard let rootController = keyWindow?.rootViewController else {
|
|
return nil
|
|
}
|
|
func findCurrentController(from vc: UIViewController?) -> UIViewController? {
|
|
if let nav = vc as? UINavigationController {
|
|
return findCurrentController(from: nav.topViewController)
|
|
} else if let tabBar = vc as? UITabBarController {
|
|
return findCurrentController(from: tabBar.selectedViewController)
|
|
} else if let presented = vc?.presentedViewController {
|
|
return findCurrentController(from: presented)
|
|
}
|
|
return vc
|
|
}
|
|
let viewController = findCurrentController(from: rootController)
|
|
return viewController
|
|
}
|
|
|
|
private func makeMainViewController() -> UIViewController {
|
|
let controller = ConferenceMainViewController()
|
|
return controller
|
|
}
|
|
|
|
private func makePopUpViewController(viewType: PopUpViewType, height: CGFloat, backgroundColor: UIColor) -> PopUpViewController {
|
|
let controller = PopUpViewController(popUpViewModelFactory: self, viewType: viewType, height: height, backgroundColor: backgroundColor)
|
|
return controller
|
|
}
|
|
|
|
private func destroyChatWindow() {
|
|
guard context.chatWindow != nil else { return }
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.context.chatWindow = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
extension RoomRouter.RoomNavigationDelegate: UINavigationControllerDelegate {
|
|
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
|
if viewController is ConferenceMainViewController {
|
|
if #available(iOS 13.0, *) {
|
|
if let appearance = RoomRouter.shared.context.appearance as? UINavigationBarAppearance {
|
|
navigationController.navigationBar.standardAppearance = appearance
|
|
navigationController.navigationBar.scrollEdgeAppearance = appearance
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension RoomRouter: PopUpViewModelFactory {
|
|
func makeRootViewModel(viewType: PopUpViewType, height: CGFloat, backgroundColor: UIColor) -> PopUpViewModel {
|
|
let viewModel = PopUpViewModel(viewType: viewType, height: height)
|
|
viewModel.backgroundColor = backgroundColor
|
|
return viewModel
|
|
}
|
|
}
|
|
|
|
extension RoomRouter: RoomKitUIEventResponder {
|
|
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
|
|
switch key {
|
|
case .TUIRoomKitService_ShowRoomVideoFloatView:
|
|
dismissPopupViewController()
|
|
popToRoomEntranceViewController()
|
|
RoomVideoFloatView.show()
|
|
case .TUIRoomKitService_ShowRoomMainView:
|
|
RoomVideoFloatView.dismiss()
|
|
self.pushMainViewController()
|
|
case .TUIRoomKitService_HiddenChatWindow:
|
|
destroyChatWindow()
|
|
case .TUIRoomKitService_DismissConferenceViewController:
|
|
dismissAllAlertController() { [weak self] in
|
|
guard let self = self else { return }
|
|
self.dismissPopupViewController() { [weak self] in
|
|
guard let self = self else { return }
|
|
self.popToRoomEntranceViewController()
|
|
}
|
|
}
|
|
default: break
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension String {
|
|
static var chatText: String {
|
|
localized("Chat")
|
|
}
|
|
}
|