QmeraVideoViewController.swift 94 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799
  1. //
  2. // VideoViewControllerQmera.swift
  3. // Qmera
  4. //
  5. // Created by Akhmad Al Qindi Irsyam on 07/10/21.
  6. //
  7. // Rn
  8. // (Kedip whiteboard)
  9. // extension Whiteboard
  10. // wbVC!.close wbTimer wbBlink
  11. import UIKit
  12. import nuSDKService
  13. import AVFoundation
  14. import NotificationBannerSwift
  15. import MediaPlayer
  16. class QmeraVideoViewController: UIViewController {
  17. static private let nMaxSPOn: Float! = 10.0
  18. static private let nMaxSPOff: Float! = 9.0
  19. static private var volumeView: MPVolumeView!
  20. static private var lastVolume: Float! = AVAudioSession.sharedInstance().outputVolume
  21. static private var bSpeakerPhone: Bool! = false
  22. static private var isLoop = false
  23. var dataPerson: [[String: String?]] = []
  24. var fPin = ""
  25. var wbRoomId = ""
  26. var isInisiator = true
  27. // var isSpeaker = false
  28. var isMuted = false
  29. var isPresent = false
  30. var callFCM = true
  31. var isNavigationHidden = false
  32. var listRemoteViewFix: [UIImageView] = [
  33. UIImageView(),
  34. UIImageView(),
  35. UIImageView(),
  36. UIImageView(),
  37. UIImageView()
  38. ]
  39. var containerLabelName: [UIView] = [
  40. UIView(),
  41. UIView(),
  42. UIView(),
  43. UIView(),
  44. UIView()
  45. ]
  46. var imageMuted: [UIImageView] = [
  47. UIImageView(),
  48. UIImageView(),
  49. UIImageView(),
  50. UIImageView(),
  51. UIImageView()
  52. ]
  53. var mutedZoom: UIImageView = {
  54. let image = UIImageView(frame: CGRect(x: 0, y: 0, width: 30, height: 40))
  55. image.contentMode = .scaleAspectFit
  56. image.image = UIImage(systemName: "mic.slash")
  57. image.tintColor = .red
  58. image.isHidden = true
  59. return image
  60. }()
  61. let myImage = UIImageView()
  62. let name = UILabel()
  63. let profileImage = UIImageView()
  64. let labelIncomingOutgoing = UILabel()
  65. let buttonDecline = UIButton()
  66. let buttonAccept = UIButton()
  67. let zoomView = UIImageView()
  68. let cameraView = UIImageView()
  69. var constraintLeadingButtonDecline = NSLayoutConstraint()
  70. var constraintBottomButtonDecline = NSLayoutConstraint()
  71. var constraintBottomStackViewToolbar = NSLayoutConstraint()
  72. var constraintLeftStackViewToolbar2 = NSLayoutConstraint()
  73. let stackViewToolbar = UIStackView()
  74. let stackViewToolbar2 = UIStackView()
  75. let stackViewToolbar3 = UIStackView()
  76. var onScreenConstraintWB = [NSLayoutConstraint]()
  77. let buttonWB = UIButton()
  78. let buttonChat = UIButton()
  79. var wbVC : WhiteboardViewController?
  80. let buttonAddParticipant = UIButton()
  81. let buttonSpeaker = UIButton()
  82. let buttonRotate = UIButton()
  83. let buttonMuted = UIButton()
  84. var showStackViewToolbar = true
  85. let scrollRemoteView = UIScrollView()
  86. var isAutoAccept = false
  87. var wbTimer = Timer()
  88. var wbBlink = false
  89. var showNotifCCEnd = false
  90. var transformZoomAfterNewUserMore2 = false
  91. var isAddCall = ""
  92. var ticketId = ""
  93. var autoAcceptAPN = false
  94. var timeStartCall = ""
  95. var idCall = ""
  96. private var frontCamera = true
  97. var users: [User] = []
  98. let poweredByView: UIStackView = {
  99. let stackView = UIStackView()
  100. stackView.axis = .horizontal
  101. stackView.spacing = 5
  102. return stackView
  103. }()
  104. private var vcTimer = Timer()
  105. private var containerTimerVC = UIView()
  106. private var labelTimerVC = UILabel()
  107. let poweredByLabel: UILabel = {
  108. let label = UILabel()
  109. label.text = "Powered by Nexilis".localized()
  110. return label
  111. }()
  112. let qmeraLogo: UIButton = {
  113. let image = UIImage(named: "Q-Button-PNG", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  114. let button = UIButton()
  115. button.setImage(image, for: .normal)
  116. button.imageView?.contentMode = .scaleAspectFit
  117. button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
  118. button.contentVerticalAlignment = .fill
  119. button.contentHorizontalAlignment = .fill
  120. // button.frame.size.width = 30
  121. // button.frame.size.height = 30
  122. return button
  123. }()
  124. let nexilisLogo: UIButton = {
  125. let image = UIImage(named: "pb_powered_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  126. let button = UIButton()
  127. button.setImage(image, for: .normal)
  128. button.imageView?.contentMode = .scaleAspectFit
  129. button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
  130. button.contentVerticalAlignment = .fill
  131. button.contentHorizontalAlignment = .fill
  132. // button.frame.size.width = 30
  133. // button.frame.size.height = 30
  134. return button
  135. }()
  136. static func turnSpeakerOn() {
  137. bSpeakerPhone = !bSpeakerPhone
  138. var bAudioEngineIsAvtive: Bool! = false
  139. repeat {
  140. API.turnSpeakerPhone(bSPon: bSpeakerPhone!)
  141. bAudioEngineIsAvtive = API.bAudioEngineIsRunning()
  142. print("Audio Session State: \(bAudioEngineIsAvtive ? "Active" : "Inactive" )")
  143. if (bAudioEngineIsAvtive) {
  144. break
  145. }
  146. } while (!bAudioEngineIsAvtive)
  147. var volume:Float! = 0
  148. if (bSpeakerPhone) {
  149. volume = lastVolume * nMaxSPOn
  150. } else {
  151. volume = lastVolume * nMaxSPOff
  152. }
  153. API.adjustVolume(fValue: volume)
  154. }
  155. // static func toggleSpeakerPhone() {
  156. // bSpeakerPhone = !bSpeakerPhone
  157. // var volume:Float! = 0
  158. // if (bSpeakerPhone) {
  159. // volume = lastVolume * nMaxSPOn
  160. // } else {
  161. // volume = lastVolume * nMaxSPOff
  162. // }
  163. // API.adjustVolume(fValue: volume)
  164. // }
  165. deinit {
  166. navigationController?.changeAppearance(clear: false)
  167. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  168. navigationController?.navigationBar.titleTextAttributes = textAttributes
  169. navigationController?.navigationBar.topItem?.backBarButtonItem = nil
  170. navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  171. NotificationCenter.default.removeObserver(self)
  172. Nexilis.floatingButton.isHidden = false
  173. Nexilis.callAPNActivated = false
  174. }
  175. override func viewWillDisappear(_ animated: Bool) {
  176. if self.isMovingFromParent {
  177. navigationController?.changeAppearance(clear: false)
  178. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  179. navigationController?.navigationBar.titleTextAttributes = textAttributes
  180. navigationController?.navigationBar.topItem?.backBarButtonItem = nil
  181. navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  182. NotificationCenter.default.removeObserver(self)
  183. }
  184. Nexilis.floatingButton.isHidden = false
  185. Nexilis.callAPNActivated = false
  186. }
  187. private func backToDefaultAudioSession() {
  188. do {
  189. let audioSession = AVAudioSession.sharedInstance()
  190. try audioSession.setCategory(.playAndRecord, mode: .default, options: .allowBluetooth)
  191. try audioSession.overrideOutputAudioPort(.speaker)
  192. try audioSession.setActive(true)
  193. } catch {
  194. }
  195. }
  196. override func viewDidLoad() {
  197. super.viewDidLoad()
  198. QmeraVideoViewController.volumeView = MPVolumeView(frame: .zero)
  199. QmeraVideoViewController.volumeView.isHidden = true
  200. Nexilis.setWhiteboardReceiver(receiver: self)
  201. Nexilis.floatingButton.isHidden = true
  202. self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
  203. navigationController?.changeAppearance(clear: true)
  204. NotificationCenter.default.addObserver(self, selector: #selector(self.onStatusCall(_:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusCall), object: nil)
  205. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
  206. NotificationCenter.default.addObserver(self, selector: #selector(onCallFCM(notification:)), name: NSNotification.Name(rawValue: Nexilis.callFCM), object: nil)
  207. view.backgroundColor = .clear
  208. navigationController?.navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
  209. navigationController?.interactivePopGestureRecognizer?.isEnabled = false
  210. navigationItem.setHidesBackButton(true, animated: false)
  211. if fPin != ""{
  212. getDataProfile(fPin: fPin)
  213. }
  214. addZoomView()
  215. addCameraView()
  216. addListRemoteView()
  217. addBackgroundIncoming()
  218. addProfileNameCalling()
  219. Calling()
  220. addToolbar()
  221. addTimerVC()
  222. addImageMutedOnZoom()
  223. if isAutoAccept {
  224. didTapAcceptCallButton()
  225. }
  226. if autoAcceptAPN {
  227. // API.receiveCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView,ivRemoteZ: zoomView)
  228. DispatchQueue.global().async {
  229. if let response1 = Nexilis.writeSync(message: CoreMessage_TMessageBank.getNotifyCalling(fPin: self.fPin, lPin: User.getMyPin()!, type: "1")) {
  230. if response1.isOk() {
  231. }
  232. }
  233. }
  234. }
  235. self.timeStartCall = String(Date().currentTimeMillis())
  236. self.idCall = (User.getMyPin() ?? "") + CoreMessage_TMessageUtil.getTID()
  237. }
  238. override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
  239. if (keyPath! == "outputVolume") {
  240. if let newKey = change?[NSKeyValueChangeKey.newKey] as? NSNumber {
  241. QmeraVideoViewController.lastVolume = newKey.floatValue
  242. if (QmeraVideoViewController.bSpeakerPhone) {
  243. let volume = QmeraVideoViewController.lastVolume * QmeraVideoViewController.nMaxSPOn
  244. API.adjustVolume(fValue: volume)
  245. } else {
  246. let volume = QmeraVideoViewController.lastVolume * QmeraVideoViewController.nMaxSPOff
  247. API.adjustVolume(fValue: volume)
  248. }
  249. }
  250. }
  251. }
  252. func getDataProfile(fPin: String) {
  253. let query = "SELECT f_pin, first_name, last_name, official_account, image_id, device_id, offline_mode, user_type FROM BUDDY where f_pin = '\(fPin)'"
  254. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  255. do {
  256. if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
  257. var row: [String: String?] = [:]
  258. if cursorData.next() {
  259. row["f_pin"] = cursorData.string(forColumnIndex: 0)
  260. var name = ""
  261. if let firstname = cursorData.string(forColumnIndex: 1) {
  262. name = firstname
  263. }
  264. if let lastname = cursorData.string(forColumnIndex: 2) {
  265. name = name + " " + lastname
  266. }
  267. row["name"] = name
  268. row["picture"] = cursorData.string(forColumnIndex: 4)
  269. row["isOfficial"] = cursorData.string(forColumnIndex: 3)
  270. row["deviceId"] = cursorData.string(forColumnIndex: 5)
  271. row["isOffline"] = cursorData.string(forColumnIndex: 6)
  272. row["user_type"] = cursorData.string(forColumnIndex: 7)
  273. if fPin != User.getMyPin() {
  274. dataPerson.append(row)
  275. }
  276. } else {
  277. var row: [String: String?] = [:]
  278. row["f_pin"] = fPin
  279. row["name"] = "User".localized()
  280. row["picture"] = ""
  281. row["isOfficial"] = ""
  282. row["deviceId"] = ""
  283. row["isOffline"] = ""
  284. row["user_type"] = ""
  285. dataPerson.append(row)
  286. }
  287. cursorData.close()
  288. } else {
  289. var row: [String: String?] = [:]
  290. row["f_pin"] = fPin
  291. row["name"] = "User".localized()
  292. row["picture"] = ""
  293. row["isOfficial"] = ""
  294. row["deviceId"] = ""
  295. row["isOffline"] = ""
  296. row["user_type"] = ""
  297. dataPerson.append(row)
  298. // if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getAddFriendQRCode(fpin: fPin)), response.isOk() {
  299. // self.getDataProfile(fPin: fPin)
  300. // }
  301. // Nexilis.addFriend (fpin: "\(fPin)") { result in
  302. // if result {
  303. // self.getDataProfile(fPin: fPin)
  304. // } else {
  305. // let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  306. // imageView.tintColor = .white
  307. // let banner = FloatingNotificationBanner(title: "Server busy, please try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  308. // banner.show()
  309. // }
  310. // }
  311. }
  312. } catch {
  313. rollback.pointee = true
  314. print("Access database error: \(error.localizedDescription)")
  315. }
  316. })
  317. }
  318. func addTimerVC() {
  319. view.addSubview(containerTimerVC)
  320. containerTimerVC.anchor(top: view.safeAreaLayoutGuide.topAnchor, centerX: view.centerXAnchor, minWidth: 40)
  321. containerTimerVC.makeRoundedView(radius: 8)
  322. containerTimerVC.backgroundColor = .black.withAlphaComponent(0.3)
  323. containerTimerVC.addSubview(labelTimerVC)
  324. labelTimerVC.anchor(left: containerTimerVC.leftAnchor, right: containerTimerVC.rightAnchor, paddingLeft: 8, paddingRight: 8, centerX: containerTimerVC.centerXAnchor, centerY: containerTimerVC.centerYAnchor)
  325. labelTimerVC.textColor = .white
  326. containerTimerVC.isHidden = true
  327. }
  328. func addImageMutedOnZoom() {
  329. view.addSubview(mutedZoom)
  330. mutedZoom.anchor(top: containerTimerVC.bottomAnchor, paddingTop: 10, centerX: view.centerXAnchor, width: 30, height: 40)
  331. }
  332. func addZoomView() {
  333. view.addSubview(zoomView)
  334. zoomView.translatesAutoresizingMaskIntoConstraints = false
  335. NSLayoutConstraint.activate([
  336. zoomView.topAnchor.constraint(equalTo: view.topAnchor),
  337. zoomView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
  338. zoomView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  339. zoomView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  340. ])
  341. zoomView.backgroundColor = .secondaryColor
  342. zoomView.isUserInteractionEnabled = true
  343. zoomView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideToolbar)))
  344. }
  345. func addCameraView() {
  346. view.addSubview(cameraView)
  347. // cameraView.frame = CGRect(x: view.frame.width - 130, y: 20, width: 120, height: 160)
  348. cameraView.translatesAutoresizingMaskIntoConstraints = false
  349. NSLayoutConstraint.activate([
  350. cameraView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
  351. cameraView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10.0),
  352. cameraView.widthAnchor.constraint(equalToConstant: 120.0),
  353. cameraView.heightAnchor.constraint(equalToConstant: 160.0)
  354. ])
  355. cameraView.backgroundColor = .secondaryColor
  356. cameraView.makeRoundedView(radius: 8)
  357. }
  358. func addListRemoteView() {
  359. view.addSubview(scrollRemoteView)
  360. scrollRemoteView.translatesAutoresizingMaskIntoConstraints = false
  361. NSLayoutConstraint.activate([
  362. scrollRemoteView.topAnchor.constraint(equalTo: cameraView.bottomAnchor, constant: 10),
  363. scrollRemoteView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
  364. scrollRemoteView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
  365. scrollRemoteView.widthAnchor.constraint(equalToConstant: 120.0)
  366. ])
  367. scrollRemoteView.showsHorizontalScrollIndicator = false
  368. scrollRemoteView.showsVerticalScrollIndicator = false
  369. scrollRemoteView.contentSize.width = 120.0
  370. scrollRemoteView.backgroundColor = .clear
  371. }
  372. func addBackgroundIncoming() {
  373. view.addSubview(myImage)
  374. myImage.translatesAutoresizingMaskIntoConstraints = false
  375. NSLayoutConstraint.activate([
  376. myImage.topAnchor.constraint(equalTo: view.topAnchor),
  377. myImage.bottomAnchor.constraint(equalTo: view.bottomAnchor),
  378. myImage.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  379. myImage.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  380. ])
  381. myImage.backgroundColor = .lightGray
  382. myImage.tintColor = .secondaryColor
  383. let image = dataPerson[0]["picture"]!!
  384. if image.isEmpty {
  385. myImage.image = UIImage(systemName: "person")
  386. myImage.contentMode = .scaleAspectFit
  387. } else {
  388. myImage.setImage(name: image)
  389. myImage.contentMode = .scaleAspectFill
  390. }
  391. // let idMe = User.getMyPin() as String?
  392. // Database().database?.inTransaction({ fmdb, rollback in
  393. // if let c = Database().getRecords(fmdb: fmdb, query: "select image_id from BUDDY where f_pin = '\(idMe!)'"), c.next() {
  394. // let image = c.string(forColumnIndex: 0)!
  395. // if image.isEmpty {
  396. // myImage.image = UIImage(systemName: "person")
  397. // myImage.contentMode = .scaleAspectFit
  398. // } else {
  399. // myImage.setImage(name: image)
  400. // myImage.contentMode = .scaleAspectFill
  401. // }
  402. // c.close()
  403. // }
  404. // })
  405. }
  406. func addProfileNameCalling() {
  407. view.addSubview(profileImage)
  408. profileImage.translatesAutoresizingMaskIntoConstraints = false
  409. profileImage.frame.size = CGSize(width: 60.0, height: 60.0)
  410. NSLayoutConstraint.activate([
  411. profileImage.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
  412. profileImage.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  413. profileImage.widthAnchor.constraint(equalToConstant: 60.0),
  414. profileImage.heightAnchor.constraint(equalToConstant: 63.0)
  415. ])
  416. profileImage.backgroundColor = .lightGray
  417. profileImage.tintColor = .secondaryColor
  418. profileImage.circle()
  419. let image = dataPerson[0]["picture"]!!
  420. if image.isEmpty {
  421. profileImage.image = UIImage(systemName: "person")
  422. profileImage.contentMode = .scaleAspectFit
  423. profileImage.layer.borderWidth = 1
  424. profileImage.layer.borderColor = UIColor.secondaryColor.cgColor
  425. } else {
  426. profileImage.setImage(name: image)
  427. profileImage.contentMode = .scaleAspectFill
  428. }
  429. view.addSubview(name)
  430. name.translatesAutoresizingMaskIntoConstraints = false
  431. NSLayoutConstraint.activate([
  432. name.topAnchor.constraint(equalTo: profileImage.bottomAnchor, constant: 5.0),
  433. name.centerXAnchor.constraint(equalTo: view.centerXAnchor)
  434. ])
  435. name.font = UIFont.systemFont(ofSize: 12)
  436. name.backgroundColor = .black.withAlphaComponent(0.05)
  437. name.layer.cornerRadius = 5.0
  438. name.clipsToBounds = true
  439. name.textColor = .mainColor
  440. name.text = dataPerson[0]["name"]!?.trimmingCharacters(in: .whitespaces)
  441. }
  442. func Calling() {
  443. view.addSubview(labelIncomingOutgoing)
  444. labelIncomingOutgoing.translatesAutoresizingMaskIntoConstraints = false
  445. NSLayoutConstraint.activate([
  446. labelIncomingOutgoing.topAnchor.constraint(equalTo: name.bottomAnchor, constant: 40.0),
  447. labelIncomingOutgoing.centerXAnchor.constraint(equalTo: view.centerXAnchor)
  448. ])
  449. if isInisiator {
  450. labelIncomingOutgoing.text = "Connecting".localized()
  451. if ticketId.isEmpty {
  452. backToDefaultAudioSession()
  453. Nexilis.playRingbacktoneCall()
  454. if callFCM {
  455. DispatchQueue.global().async {
  456. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getCalling(fPin: self.dataPerson[0]["f_pin"]!!, type: "2"), timeout: 30 * 1000) {
  457. if response.isOk() {
  458. } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "01" && self.dataPerson.count > 0 {
  459. API.initiateCCall(sParty: self.dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: self.listRemoteViewFix, ivLocalView: self.cameraView, ivRemoteZ: self.zoomView)
  460. } else {
  461. DispatchQueue.main.async {
  462. if self.labelIncomingOutgoing.isDescendant(of: self.view) {
  463. self.labelIncomingOutgoing.text = "Busy".localized()
  464. }
  465. if self.buttonDecline.isDescendant(of: self.view) {
  466. self.buttonDecline.removeFromSuperview()
  467. }
  468. if self.buttonAccept.isDescendant(of: self.view) {
  469. self.buttonAccept.removeFromSuperview()
  470. }
  471. }
  472. }
  473. } else {
  474. let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
  475. imageView.tintColor = .white
  476. let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
  477. banner.show()
  478. }
  479. }
  480. } else {
  481. API.initiateCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
  482. }
  483. } else {
  484. API.ccs(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: true)
  485. if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getIncomingCallCS(f_pin_opposite: users[0].pin), timeout: 30 * 1000){
  486. if response.mBodies[CoreMessage_TMessageKey.ERRCOD] != "01" {
  487. endAllCall()
  488. }
  489. }
  490. }
  491. } else {
  492. if ticketId.isEmpty {
  493. backToDefaultAudioSession()
  494. Nexilis.playRingtoneCall()
  495. }
  496. labelIncomingOutgoing.text = "Incoming video call".localized() + "..."
  497. }
  498. labelIncomingOutgoing.font = UIFont.systemFont(ofSize: 12)
  499. labelIncomingOutgoing.backgroundColor = .black.withAlphaComponent(0.05)
  500. labelIncomingOutgoing.layer.cornerRadius = 5.0
  501. labelIncomingOutgoing.clipsToBounds = true
  502. labelIncomingOutgoing.textColor = .mainColor
  503. }
  504. func addToolbar() {
  505. view.addSubview(buttonDecline)
  506. buttonDecline.translatesAutoresizingMaskIntoConstraints = false
  507. buttonDecline.frame.size = CGSize(width: 70.0, height: 70.0)
  508. if isInisiator {
  509. constraintLeadingButtonDecline = buttonDecline.centerXAnchor.constraint(equalTo: view.centerXAnchor)
  510. } else {
  511. constraintLeadingButtonDecline = buttonDecline.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: view.frame.width * 0.2)
  512. }
  513. constraintBottomButtonDecline = buttonDecline.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60.0)
  514. NSLayoutConstraint.activate([
  515. constraintBottomButtonDecline,
  516. constraintLeadingButtonDecline,
  517. buttonDecline.widthAnchor.constraint(equalToConstant: 70.0),
  518. buttonDecline.heightAnchor.constraint(equalToConstant: 70.0)
  519. ])
  520. buttonDecline.backgroundColor = .red
  521. buttonDecline.circle()
  522. buttonDecline.setImage(UIImage(systemName: "xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  523. buttonDecline.tintColor = .white
  524. buttonDecline.addTarget(self, action: #selector(didTapDeclineCallButton(sender:)), for: .touchUpInside)
  525. if !isInisiator{
  526. view.addSubview(buttonAccept)
  527. buttonAccept.translatesAutoresizingMaskIntoConstraints = false
  528. buttonAccept.frame.size = CGSize(width: 70.0, height: 70.0)
  529. NSLayoutConstraint.activate([
  530. buttonAccept.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60.0),
  531. buttonAccept.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -(view.frame.width * 0.2)),
  532. buttonAccept.widthAnchor.constraint(equalToConstant: 70.0),
  533. buttonAccept.heightAnchor.constraint(equalToConstant: 70.0)
  534. ])
  535. buttonAccept.backgroundColor = .greenColor
  536. buttonAccept.circle()
  537. buttonAccept.setImage(UIImage(systemName: "checkmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  538. buttonAccept.tintColor = .white
  539. buttonAccept.addTarget(self, action: #selector(didTapAcceptCallButton), for: .touchUpInside)
  540. }
  541. }
  542. @objc func onCallFCM(notification: NSNotification) {
  543. DispatchQueue.main.async { [self] in
  544. let data:[AnyHashable : Any] = notification.userInfo!
  545. if let l_pin = data["l_pin"] as? String {
  546. if let f_pin = data["f_pin"] as? String {
  547. if f_pin == User.getMyPin()! {
  548. labelIncomingOutgoing.text = "Waiting for answer".localized()
  549. API.initiateCCall(sParty: l_pin, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
  550. }
  551. }
  552. }
  553. }
  554. }
  555. @objc func onReceiveMessage(notification: NSNotification) {
  556. DispatchQueue.main.async {
  557. let data:[AnyHashable : Any] = notification.userInfo!
  558. if let dataMessage = data["message"] as? TMessage {
  559. if (dataMessage.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER) {
  560. let data = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  561. if !data.isEmpty {
  562. if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
  563. var members = ""
  564. let idMe = User.getMyPin()!
  565. for json in jsonArray {
  566. if "\(json)" != idMe {
  567. if members.isEmpty {
  568. members = "\(json)"
  569. } else {
  570. members += ",\(json)"
  571. }
  572. }
  573. }
  574. SecureUserDefaults.shared.set(members, forKey: "inEditorPersonal")
  575. SecureUserDefaults.shared.set("\(members)", forKey: "membersCC")
  576. }
  577. }
  578. self.users.append(User.getData(pin: dataMessage.getPIN())!)
  579. }
  580. }
  581. }
  582. }
  583. private func makeStateCall() {
  584. var longCall = "0"
  585. if self.vcTimer.isValid {
  586. longCall = self.labelTimerVC.text ?? ""
  587. }
  588. if self.isInisiator {
  589. Nexilis.saveMessageCall(idCall: self.idCall, textMessage: "Outgoing video call" + " at \(longCall)", fPin: User.getMyPin() ?? "", lPin: self.dataPerson[0]["f_pin"]!!, timeCall: self.timeStartCall, attachment_type: MessageScope.CALL)
  590. } else {
  591. if !self.vcTimer.isValid {
  592. Nexilis.saveMessageCall(idCall: self.idCall, textMessage: "Missed video call" + " at 0", fPin: self.dataPerson[0]["f_pin"]!!, lPin: User.getMyPin() ?? "", timeCall: self.timeStartCall, attachment_type: MessageScope.MISSED_CALL)
  593. } else {
  594. Nexilis.saveMessageCall(idCall: self.idCall, textMessage: "Incoming video call" + " at \(longCall)", fPin: self.dataPerson[0]["f_pin"]!!, lPin: User.getMyPin() ?? "", timeCall: self.timeStartCall, attachment_type: MessageScope.CALL)
  595. }
  596. }
  597. }
  598. @objc func didTapDeclineCallButton(sender: AnyObject){
  599. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  600. if !onGoingCC.isEmpty {
  601. let alert = LibAlertController(title: "Interaction with Call Center is in progress".localized(), message: "Are you sure you want to end the Call Center?".localized(), preferredStyle: .alert)
  602. alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
  603. alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {(_) in
  604. if(!self.wbRoomId.isEmpty){
  605. DispatchQueue.main.async {
  606. self.wbTimer.invalidate()
  607. _ = Nexilis.getWhiteboardDelegate()?.terminate()
  608. }
  609. }
  610. Nexilis.stopRingtoneCall()
  611. Nexilis.stopRingbacktoneCall()
  612. QmeraVideoViewController.isLoop = false
  613. self.endAllCall()
  614. self.dismiss(animated: true, completion: nil)
  615. }))
  616. self.present(alert, animated: true, completion: nil)
  617. } else {
  618. let alert = LibAlertController(title: "End Video Call".localized(), message: "Are you sure you want to end video call?".localized(), preferredStyle: .alert)
  619. alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
  620. alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {(_) in
  621. self.makeStateCall()
  622. Nexilis.stopRingtoneCall()
  623. Nexilis.stopRingbacktoneCall()
  624. // if self.labelIncomingOutgoing.isDescendant(of: self.view) {
  625. // self.labelIncomingOutgoing.text = "Video call is over".localized()
  626. // }
  627. if self.stackViewToolbar.isDescendant(of: self.view){
  628. self.stackViewToolbar.removeFromSuperview()
  629. }
  630. if self.stackViewToolbar2.isDescendant(of: self.view){
  631. self.stackViewToolbar2.removeFromSuperview()
  632. }
  633. if self.buttonWB.isDescendant(of: self.view){
  634. self.buttonWB.removeFromSuperview()
  635. }
  636. if self.buttonChat.isDescendant(of: self.view){
  637. self.buttonChat.removeFromSuperview()
  638. }
  639. if self.buttonDecline.isDescendant(of: self.view) {
  640. self.buttonDecline.removeFromSuperview()
  641. }
  642. if self.buttonAccept.isDescendant(of: self.view) {
  643. self.buttonAccept.removeFromSuperview()
  644. }
  645. if self.buttonRotate.isDescendant(of: self.view) {
  646. self.buttonRotate.removeFromSuperview()
  647. }
  648. if self.buttonMuted.isDescendant(of: self.view) {
  649. self.buttonMuted.removeFromSuperview()
  650. }
  651. if self.wbVC != nil{
  652. self.wbVC!.close?()
  653. }
  654. self.wbTimer.invalidate()
  655. self.vcTimer.invalidate()
  656. // self.labelTimerVC.text = "Video call is over".localized()
  657. QmeraVideoViewController.isLoop = false
  658. self.endAllCall()
  659. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  660. if self.isInisiator && !self.isPresent {
  661. self.navigationController?.popViewController(animated: true)
  662. } else {
  663. self.dismiss(animated: true, completion: nil)
  664. }
  665. }
  666. }))
  667. self.present(alert, animated: true, completion: nil)
  668. }
  669. }
  670. @objc func didTapAcceptCallButton() {
  671. if !isInisiator{
  672. let goAudioCall = Nexilis.checkMicPermission()
  673. let goVideoCall = Nexilis.checkCameraPermission()
  674. if goVideoCall == 0 {
  675. let alert = LibAlertController(title: "Attention!".localized(), message: !goAudioCall && goVideoCall == 0 ? "Please allow microphone & camera permission in your settings".localized() : !goAudioCall ? "Please allow microphone permission in your settings".localized() : "Please allow camera permission in your settings", preferredStyle: .alert)
  676. alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {_ in
  677. if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
  678. UIApplication.shared.open(url, options: [:], completionHandler: nil)
  679. }
  680. }))
  681. self.navigationController?.present(alert, animated: true, completion: nil)
  682. return
  683. } else if goVideoCall == -1 {
  684. return
  685. }
  686. // Nexilis.startAudio()
  687. if ticketId.isEmpty {
  688. API.receiveCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView,ivRemoteZ: zoomView)
  689. } else {
  690. API.csa(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: true)
  691. }
  692. }
  693. DispatchQueue.main.async {
  694. self.myImage.removeFromSuperview()
  695. self.name.removeFromSuperview()
  696. self.profileImage.removeFromSuperview()
  697. self.labelIncomingOutgoing.removeFromSuperview()
  698. self.buttonAccept.removeFromSuperview()
  699. NSLayoutConstraint.deactivate([
  700. self.constraintLeadingButtonDecline,
  701. self.constraintBottomButtonDecline
  702. ])
  703. self.addToolbarAfterAccept()
  704. self.buttonDecline.setImage(UIImage(systemName: "phone.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  705. UIView.animate(withDuration: 1.0, animations: {
  706. self.view.layoutIfNeeded()
  707. })
  708. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  709. self.containerTimerVC.isHidden = false
  710. self.buttonRotate.isHidden = false
  711. self.buttonAddParticipant.isHidden = false
  712. self.buttonSpeaker.isHidden = false
  713. self.buttonWB.isHidden = false
  714. self.buttonChat.isHidden = false
  715. self.poweredByView.isHidden = false
  716. self.buttonMuted.isHidden = false
  717. let connectDate = Date()
  718. self.vcTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
  719. let format = Utils.callDurationFormatter.string(from: Date().timeIntervalSince(connectDate))
  720. self.labelTimerVC.text = format
  721. }
  722. self.vcTimer.fire()
  723. // self.setSpeaker()
  724. }
  725. }
  726. }
  727. @objc func didTapChatButton(){
  728. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  729. let members: String = SecureUserDefaults.shared.value(forKey: "membersCC") ?? ""
  730. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  731. let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
  732. editorPersonalVC.hidesBottomBarWhenPushed = true
  733. editorPersonalVC.unique_l_pin = officer
  734. editorPersonalVC.fromNotification = true
  735. editorPersonalVC.isContactCenter = true
  736. editorPersonalVC.fPinContacCenter = members
  737. editorPersonalVC.complaintId = ticketId
  738. editorPersonalVC.onGoingCC = true
  739. editorPersonalVC.isRequestContactCenter = false
  740. editorPersonalVC.channelContactCenter = "2"
  741. editorPersonalVC.users = users
  742. editorPersonalVC.fromVCAC = true
  743. let navigationController = CustomNavigationController(rootViewController: editorPersonalVC)
  744. navigationController.modalPresentationStyle = .overCurrentContext
  745. navigationController.navigationBar.tintColor = .white
  746. navigationController.navigationBar.barTintColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
  747. navigationController.navigationBar.isTranslucent = false
  748. navigationController.navigationBar.overrideUserInterfaceStyle = .dark
  749. navigationController.navigationBar.barStyle = .black
  750. let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
  751. UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
  752. let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
  753. navigationController.navigationBar.titleTextAttributes = textAttributes
  754. if UIApplication.shared.visibleViewController?.navigationController != nil {
  755. UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
  756. } else {
  757. UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
  758. }
  759. }
  760. @objc func didTapWBButton(){
  761. if(wbVC == nil){
  762. wbVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "wbVC") as? WhiteboardViewController
  763. if(wbRoomId.isEmpty){
  764. let me = User.getMyPin()!
  765. let tid = CoreMessage_TMessageUtil.getTID()
  766. wbRoomId = "\(me)wbvc\(tid)"
  767. wbVC!.roomId = wbRoomId
  768. var destinations = [String]()
  769. var destString = ""
  770. for d in dataPerson{
  771. destinations.append(d["f_pin"]!!)
  772. if destString.isEmpty{
  773. destString = d["f_pin"]!!
  774. } else {
  775. destString = destString + ",\(d["f_pin"]!!)"
  776. }
  777. }
  778. wbVC!.destinations = destinations
  779. wbVC!.sendInit()
  780. SecureUserDefaults.shared.set("\(me),\(destString)", forKey: "wb_vc")
  781. }
  782. else {
  783. self.wbTimer.invalidate()
  784. self.buttonWB.backgroundColor = .lightGray
  785. wbVC!.roomId = wbRoomId
  786. wbVC!.sendJoin()
  787. }
  788. }
  789. wbVC!.close = {
  790. DispatchQueue.main.async {
  791. if self.wbVC!.view.isDescendant(of: self.view){
  792. self.wbVC!.view.removeFromSuperview()
  793. }
  794. self.buttonDecline.isHidden = false
  795. self.buttonSpeaker.isHidden = false
  796. self.buttonAddParticipant.isHidden = false
  797. self.buttonRotate.isHidden = false
  798. self.buttonMuted.isHidden = false
  799. // if(!self.wbRoomId.isEmpty){
  800. // DispatchQueue.main.async {
  801. // self.wbTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.runTimer), userInfo: nil, repeats: true)
  802. // }
  803. // }
  804. }
  805. }
  806. self.buttonDecline.isHidden = true
  807. self.buttonSpeaker.isHidden = true
  808. self.buttonMuted.isHidden = true
  809. self.buttonAddParticipant.isHidden = true
  810. self.buttonRotate.isHidden = true
  811. addChild(wbVC!)
  812. wbVC!.view.translatesAutoresizingMaskIntoConstraints = false
  813. view.addSubview(wbVC!.view)
  814. onScreenConstraintWB = [
  815. wbVC!.view.topAnchor.constraint(equalTo: self.view.topAnchor),
  816. wbVC!.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
  817. wbVC!.view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
  818. wbVC!.view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
  819. ]
  820. NSLayoutConstraint.activate(onScreenConstraintWB)
  821. // Notify the child view controller that the move is complete.
  822. wbVC!.didMove(toParent: self)
  823. // self.navigationController?.setNavigationBarHidden(false, animated: true)
  824. // controller.modalPresentationStyle = .overCurrentContext
  825. // self.navigationController?.present(controller, animated: true)
  826. }
  827. func addToolbarAfterAccept() {
  828. Nexilis.stopRingtoneCall()
  829. view.addSubview(self.stackViewToolbar)
  830. self.stackViewToolbar.translatesAutoresizingMaskIntoConstraints = false
  831. constraintBottomStackViewToolbar = self.stackViewToolbar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -60.0)
  832. NSLayoutConstraint.activate([
  833. self.stackViewToolbar.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
  834. constraintBottomStackViewToolbar
  835. ])
  836. self.stackViewToolbar.axis = .horizontal
  837. self.stackViewToolbar.distribution = .equalSpacing
  838. self.stackViewToolbar.alignment = .center
  839. self.stackViewToolbar.spacing = 30
  840. view.addSubview(self.stackViewToolbar3)
  841. self.stackViewToolbar3.anchor(bottom: self.stackViewToolbar.topAnchor, paddingBottom: 10, centerX: self.view.centerXAnchor)
  842. self.stackViewToolbar3.axis = .horizontal
  843. self.stackViewToolbar3.distribution = .equalSpacing
  844. self.stackViewToolbar3.alignment = .center
  845. self.stackViewToolbar3.spacing = 30
  846. view.addSubview(buttonRotate)
  847. buttonRotate.translatesAutoresizingMaskIntoConstraints = false
  848. buttonRotate.frame.size = CGSize(width: 70.0, height: 70.0)
  849. NSLayoutConstraint.activate([
  850. buttonRotate.widthAnchor.constraint(equalToConstant: 70.0),
  851. buttonRotate.heightAnchor.constraint(equalToConstant: 70.0),
  852. ])
  853. buttonRotate.backgroundColor = .secondaryColor
  854. buttonRotate.setImage(UIImage(systemName: "arrow.triangle.2.circlepath.camera", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  855. buttonRotate.tintColor = .mainColor
  856. buttonRotate.circle()
  857. buttonRotate.isHidden = true
  858. buttonRotate.addTarget(self, action: #selector(camera(sender:)), for: .touchUpInside)
  859. view.addSubview(buttonMuted)
  860. buttonMuted.translatesAutoresizingMaskIntoConstraints = false
  861. buttonMuted.frame.size = CGSize(width: 70.0, height: 70.0)
  862. NSLayoutConstraint.activate([
  863. buttonMuted.widthAnchor.constraint(equalToConstant: 70.0),
  864. buttonMuted.heightAnchor.constraint(equalToConstant: 70.0),
  865. ])
  866. buttonMuted.backgroundColor = .secondaryColor
  867. buttonMuted.setImage(UIImage(systemName: "mic", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  868. buttonMuted.tintColor = .mainColor
  869. buttonMuted.circle()
  870. buttonMuted.isHidden = true
  871. buttonMuted.addTarget(self, action: #selector(muted(sender:)), for: .touchUpInside)
  872. view.addSubview(buttonAddParticipant)
  873. buttonAddParticipant.translatesAutoresizingMaskIntoConstraints = false
  874. buttonAddParticipant.frame.size = CGSize(width: 70.0, height: 70.0)
  875. NSLayoutConstraint.activate([
  876. buttonAddParticipant.widthAnchor.constraint(equalToConstant: 70.0),
  877. buttonAddParticipant.heightAnchor.constraint(equalToConstant: 70.0)
  878. ])
  879. buttonAddParticipant.backgroundColor = .secondaryColor
  880. buttonAddParticipant.setImage(UIImage(systemName: "person.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  881. buttonAddParticipant.tintColor = .mainColor
  882. buttonAddParticipant.circle()
  883. buttonAddParticipant.isHidden = true
  884. buttonAddParticipant.addTarget(self, action: #selector(didTapAddParticipantButton(sender:)), for: .touchUpInside)
  885. view.addSubview(buttonSpeaker)
  886. buttonSpeaker.translatesAutoresizingMaskIntoConstraints = false
  887. buttonSpeaker.frame.size = CGSize(width: 70.0, height: 70.0)
  888. NSLayoutConstraint.activate([
  889. buttonSpeaker.widthAnchor.constraint(equalToConstant: 70.0),
  890. buttonSpeaker.heightAnchor.constraint(equalToConstant: 70.0)
  891. ])
  892. buttonSpeaker.backgroundColor = .secondaryColor
  893. buttonSpeaker.tintColor = .mainColor
  894. buttonSpeaker.setImage(UIImage(systemName: "speaker.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  895. buttonSpeaker.circle()
  896. buttonSpeaker.isHidden = true
  897. buttonSpeaker.addTarget(self, action: #selector(didTapSpeakerButton(sender:)), for: .touchUpInside)
  898. view.addSubview(self.stackViewToolbar2)
  899. self.stackViewToolbar2.translatesAutoresizingMaskIntoConstraints = false
  900. constraintLeftStackViewToolbar2 = self.stackViewToolbar2.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0)
  901. NSLayoutConstraint.activate([
  902. self.stackViewToolbar2.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
  903. constraintLeftStackViewToolbar2
  904. ])
  905. self.stackViewToolbar2.axis = .vertical
  906. self.stackViewToolbar2.distribution = .equalSpacing
  907. self.stackViewToolbar2.alignment = .center
  908. self.stackViewToolbar2.spacing = 5
  909. view.addSubview(buttonWB)
  910. buttonWB.translatesAutoresizingMaskIntoConstraints = false
  911. buttonWB.frame.size = CGSize(width: 40.0, height: 40.0)
  912. NSLayoutConstraint.activate([
  913. buttonWB.widthAnchor.constraint(equalToConstant: 40.0),
  914. buttonWB.heightAnchor.constraint(equalToConstant: 40.0)
  915. ])
  916. buttonWB.backgroundColor = .lightGray
  917. buttonWB.setImage(UIImage(systemName: "ipad.landscape", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium, scale: .default)), for: .normal)
  918. buttonWB.circle()
  919. buttonWB.tintColor = .black
  920. buttonWB.isHidden = true
  921. buttonWB.addTarget(self, action: #selector(didTapWBButton), for: .touchUpInside)
  922. if !ticketId.isEmpty {
  923. view.addSubview(buttonChat)
  924. buttonChat.translatesAutoresizingMaskIntoConstraints = false
  925. buttonChat.frame.size = CGSize(width: 40.0, height: 40.0)
  926. NSLayoutConstraint.activate([
  927. buttonChat.widthAnchor.constraint(equalToConstant: 40.0),
  928. buttonChat.heightAnchor.constraint(equalToConstant: 40.0)
  929. ])
  930. buttonChat.backgroundColor = .lightGray
  931. buttonChat.setImage(UIImage(systemName: "bubble.right", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .medium, scale: .default)), for: .normal)
  932. buttonChat.circle()
  933. buttonChat.tintColor = .black
  934. buttonChat.isHidden = true
  935. buttonChat.addTarget(self, action: #selector(didTapChatButton), for: .touchUpInside)
  936. }
  937. view.addSubview(poweredByView)
  938. self.poweredByView.translatesAutoresizingMaskIntoConstraints = false
  939. let constraintRightPowered = self.poweredByView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0)
  940. let constraintBottomPowered = self.poweredByView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0)
  941. NSLayoutConstraint.activate([
  942. constraintRightPowered,
  943. constraintBottomPowered,
  944. nexilisLogo.widthAnchor.constraint(equalToConstant: 30.0),
  945. nexilisLogo.heightAnchor.constraint(equalToConstant: 30.0)
  946. ])
  947. poweredByView.addArrangedSubview(poweredByLabel)
  948. poweredByView.addArrangedSubview(nexilisLogo)
  949. poweredByView.isHidden = true
  950. stackViewToolbar.addArrangedSubview(buttonAddParticipant)
  951. stackViewToolbar.addArrangedSubview(buttonDecline)
  952. stackViewToolbar.addArrangedSubview(buttonSpeaker)
  953. stackViewToolbar2.addArrangedSubview(buttonWB)
  954. stackViewToolbar2.addArrangedSubview(buttonChat)
  955. stackViewToolbar3.addArrangedSubview(buttonRotate)
  956. stackViewToolbar3.addArrangedSubview(buttonMuted)
  957. // startFaceTimer()
  958. }
  959. func endAllCall() {
  960. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  961. if !onGoingCC.isEmpty {
  962. let requester = onGoingCC.components(separatedBy: ",")[0]
  963. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  964. let complaintId = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2]
  965. let idMe = User.getMyPin()!
  966. let startTimeCC: String = SecureUserDefaults.shared.value(forKey: "startTimeCC") ?? ""
  967. DispatchQueue.global().async {
  968. let date = "\(Date().currentTimeMillis())"
  969. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  970. do {
  971. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  972. "type" : "2",
  973. "title" : "Contact Center".localized(),
  974. "time" : startTimeCC,
  975. "f_pin" : officer,
  976. "data" : complaintId,
  977. "time_end" : date,
  978. "complaint_id" : complaintId,
  979. "members" : "",
  980. "requester": requester
  981. ], replace: true)
  982. } catch {
  983. rollback.pointee = true
  984. print("Access database error: \(error.localizedDescription)")
  985. }
  986. })
  987. if officer == idMe {
  988. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: requester))
  989. } else {
  990. if requester == idMe {
  991. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: officer))
  992. } else {
  993. _ = Nexilis.write(message: CoreMessage_TMessageBank.leaveCCRoomInvite(ticket_id: complaintId))
  994. }
  995. }
  996. SecureUserDefaults.shared.removeValue(forKey: "onGoingCC")
  997. SecureUserDefaults.shared.removeValue(forKey: "membersCC")
  998. SecureUserDefaults.shared.removeValue(forKey: "startTimeCC")
  999. SecureUserDefaults.shared.removeValue(forKey: "waitingRequestCC")
  1000. }
  1001. }
  1002. API.terminateCall(sParty: nil)
  1003. cameraView.image = nil
  1004. zoomView.image = nil
  1005. listRemoteViewFix.removeAll()
  1006. dataPerson.removeAll()
  1007. }
  1008. func setSpeaker() {
  1009. DispatchQueue.main.async {
  1010. QmeraVideoViewController.turnSpeakerOn()
  1011. if (QmeraVideoViewController.bSpeakerPhone) {
  1012. self.buttonSpeaker.backgroundColor = .lightGray
  1013. self.buttonSpeaker.tintColor = .mainColor
  1014. self.buttonSpeaker.setImage(UIImage(systemName: "speaker.wave.2", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  1015. } else {
  1016. self.buttonSpeaker.backgroundColor = .secondaryColor
  1017. self.buttonSpeaker.tintColor = .mainColor
  1018. self.buttonSpeaker.setImage(UIImage(systemName: "speaker.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  1019. }
  1020. }
  1021. }
  1022. @objc func didTapSpeakerButton(sender: AnyObject){
  1023. setSpeaker()
  1024. }
  1025. @objc func didTapAddParticipantButton(sender: AnyObject){
  1026. if let contactViewController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "contactSID") as? ContactCallViewController {
  1027. contactViewController.isAddParticipantVideo = true
  1028. contactViewController.connectedCall = dataPerson
  1029. contactViewController.isDismiss = { data in
  1030. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1031. if !onGoingCC.isEmpty {
  1032. DispatchQueue.global().async {
  1033. _ = Nexilis.write(message: CoreMessage_TMessageBank.getCCRoomInvite(l_pin: data["f_pin"]!!, ticket_id: onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2], channel: "2"))
  1034. }
  1035. DispatchQueue.main.async {
  1036. self.isAddCall = data["f_pin"]!!
  1037. }
  1038. } else {
  1039. DispatchQueue.main.async {
  1040. self.dataPerson.append(data)
  1041. API.initiateCCall(sParty: data["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: self.listRemoteViewFix, ivLocalView: self.cameraView, ivRemoteZ: self.zoomView)
  1042. }
  1043. }
  1044. }
  1045. present(CustomNavigationController(rootViewController: contactViewController), animated: true, completion: nil)
  1046. }
  1047. }
  1048. @objc func camera(sender: Any?) {
  1049. DispatchQueue.global().async { [self] in
  1050. if frontCamera {
  1051. API.changeCameraParam(nCameraIdx: 0, nResolutionIndex: 2, nQuality: 4)
  1052. frontCamera = false
  1053. } else {
  1054. API.changeCameraParam(nCameraIdx: 1, nResolutionIndex: 2, nQuality: 4)
  1055. frontCamera = true
  1056. }
  1057. }
  1058. }
  1059. @objc func muted(sender: Any?) {
  1060. isMuted = !isMuted
  1061. API.mmc(int: 1, boolean: isMuted)
  1062. DispatchQueue.main.async {
  1063. if (self.isMuted) {
  1064. self.buttonMuted.backgroundColor = .lightGray
  1065. self.buttonMuted.tintColor = .mainColor
  1066. self.buttonMuted.setImage(UIImage(systemName: "mic.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  1067. } else {
  1068. self.buttonMuted.backgroundColor = .secondaryColor
  1069. self.buttonMuted.tintColor = .mainColor
  1070. self.buttonMuted.setImage(UIImage(systemName: "mic", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .medium, scale: .default)), for: .normal)
  1071. }
  1072. }
  1073. }
  1074. @objc func hideToolbar() {
  1075. DispatchQueue.main.async {
  1076. if self.showStackViewToolbar {
  1077. self.showStackViewToolbar = false
  1078. self.constraintBottomStackViewToolbar.constant = 150
  1079. self.constraintLeftStackViewToolbar2.constant = -60
  1080. UIView.animate(withDuration: 0.35, animations: {
  1081. self.view.layoutIfNeeded()
  1082. })
  1083. } else {
  1084. self.showStackViewToolbar = true
  1085. self.constraintBottomStackViewToolbar.constant = -60
  1086. self.constraintLeftStackViewToolbar2.constant = 10
  1087. UIView.animate(withDuration: 0.35, animations: {
  1088. self.view.layoutIfNeeded()
  1089. })
  1090. }
  1091. }
  1092. }
  1093. @objc func onStatusCall(_ notification: NSNotification) {
  1094. let data = notification.userInfo
  1095. let state = (data?["state"] ?? 0) as! Int
  1096. let message = (data?["message"] ?? "") as! String
  1097. var remoteChannel = [String:String]()
  1098. let arrayMessage = message.split(separator: ",")
  1099. if state == Nexilis.AUDIO_VIDEO_CALL_MUTED {
  1100. DispatchQueue.main.async { [self] in
  1101. if self.dataPerson.count == 1 {
  1102. if arrayMessage[1] == "1" {
  1103. mutedZoom.isHidden = false
  1104. } else {
  1105. mutedZoom.isHidden = true
  1106. }
  1107. }
  1108. }
  1109. } else if (state == Nexilis.VIDEO_CALL_ZOOM) && self.dataPerson.count > 1 {
  1110. DispatchQueue.main.async {
  1111. if arrayMessage[0] == arrayMessage[3] {
  1112. self.zoomView.transform = CGAffineTransform.init(scaleX: -1.9, y: 2.2).rotated(by: (-CGFloat.pi)/2)
  1113. self.zoomView.contentMode = .scaleAspectFit
  1114. } else {
  1115. self.zoomView.transform = CGAffineTransform.init(scaleX: -1.9, y: 2.2).rotated(by: (CGFloat.pi)/2)
  1116. self.zoomView.contentMode = .scaleAspectFit
  1117. }
  1118. }
  1119. } else if (state == Nexilis.VIDEO_CAMERA_PARAMS_CHANED){
  1120. if(arrayMessage[3] == "0"){
  1121. DispatchQueue.main.async {
  1122. if self.dataPerson.count == 1 && arrayMessage[2] == "1" && arrayMessage[4] == "1" {
  1123. self.zoomView.transform = CGAffineTransform.init(scaleX: 1.9, y: 2.2).rotated(by: (-CGFloat.pi)/2)
  1124. } else {
  1125. self.zoomView.transform = CGAffineTransform.init(scaleX: 1.9, y: 2.2).rotated(by: (CGFloat.pi)/2)
  1126. }
  1127. }
  1128. }
  1129. }
  1130. else if state == Nexilis.STREAMING_SEMINAR_ENDED { // always call turnspeaker
  1131. QmeraVideoViewController.isLoop = true
  1132. DispatchQueue.global(qos: .userInitiated).async {
  1133. repeat {
  1134. Thread.sleep(forTimeInterval : 1)
  1135. if (QmeraVideoViewController.isLoop && !API.bAudioEngineIsRunning()) {
  1136. API.turnSpeakerPhone(bSPon: QmeraVideoViewController.bSpeakerPhone!)
  1137. }
  1138. } while (QmeraVideoViewController.isLoop)
  1139. }
  1140. // DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: {
  1141. // API.turnSpeakerPhone(bSPon: QmeraAudioViewController.bSpeakerPhone!)
  1142. // })
  1143. // DispatchQueue.global().async {
  1144. // var bAudioSessionIsAvtive: Bool! = false
  1145. // repeat {
  1146. // API.turnSpeakerPhone(bSPon: QmeraAudioViewController.bSpeakerPhone!)
  1147. // let audioSession = AVAudioSession.sharedInstance()
  1148. // bAudioSessionIsAvtive = !audioSession.secondaryAudioShouldBeSilencedHint
  1149. // print("repeat turnSpeakerPhone >> \(bAudioSessionIsAvtive)")
  1150. // if (bAudioSessionIsAvtive) {
  1151. // break
  1152. // }
  1153. // } while (!bAudioSessionIsAvtive)
  1154. // }
  1155. }
  1156. else if (state == Nexilis.VIDEO_CALL_OFFHOOK) {
  1157. DispatchQueue.main.async {
  1158. Nexilis.stopRingbacktoneCall()
  1159. }
  1160. let channel = arrayMessage[3]
  1161. remoteChannel[String(channel)] = String(arrayMessage[5])
  1162. DispatchQueue.main.async {
  1163. if (self.dataPerson.count == 1 && String(arrayMessage[1]) != self.dataPerson[0]["f_pin"]!!) {
  1164. self.getDataProfile(fPin: String(arrayMessage[1]))
  1165. for i in 0...1 {
  1166. self.scrollRemoteView.addSubview(self.listRemoteViewFix[i])
  1167. self.listRemoteViewFix[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 160)
  1168. self.listRemoteViewFix[i].backgroundColor = .clear
  1169. self.listRemoteViewFix[i].makeRoundedView(radius: 8.0)
  1170. self.scrollRemoteView.addSubview(self.containerLabelName[i])
  1171. self.containerLabelName[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 30)
  1172. self.containerLabelName[i].backgroundColor = .orangeBNI.withAlphaComponent(0.5)
  1173. self.containerLabelName[i].makeRoundedView(radius: 8.0)
  1174. if i == 0 {
  1175. if self.dataPerson[0]["user_type"] == "2" {
  1176. self.listRemoteViewFix[0].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (CGFloat.pi)/2)
  1177. } else {
  1178. self.listRemoteViewFix[0].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (-CGFloat.pi)/2)
  1179. }
  1180. } else {
  1181. if arrayMessage[5] == "2" {
  1182. self.listRemoteViewFix[1].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (CGFloat.pi)/2)
  1183. } else {
  1184. self.listRemoteViewFix[1].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (-CGFloat.pi)/2)
  1185. }
  1186. }
  1187. let pictureImage = self.dataPerson[i]["picture"] ?? ""
  1188. let namePerson = self.dataPerson[i]["name"] ?? ""
  1189. if (!pictureImage!.isEmpty) {
  1190. self.listRemoteViewFix[i].setImage(name: pictureImage!)
  1191. self.listRemoteViewFix[i].contentMode = .scaleAspectFill
  1192. } else {
  1193. self.listRemoteViewFix[i].image = UIImage(systemName: "person")
  1194. self.listRemoteViewFix[i].backgroundColor = UIColor.systemGray6
  1195. self.listRemoteViewFix[i].contentMode = .scaleAspectFit
  1196. }
  1197. let labelName = UILabel()
  1198. self.containerLabelName[i].addSubview(labelName)
  1199. labelName.anchor(left: self.containerLabelName[i].leftAnchor, right: self.containerLabelName[i].rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: self.containerLabelName[i].centerXAnchor, centerY: self.containerLabelName[i].centerYAnchor)
  1200. labelName.text = namePerson
  1201. labelName.textAlignment = .center
  1202. labelName.textColor = .white
  1203. }
  1204. self.scrollRemoteView.contentSize.height = CGFloat(170 * 2)
  1205. if self.buttonWB.isEnabled {
  1206. self.buttonWB.isEnabled = false
  1207. }
  1208. if self.buttonRotate.isEnabled {
  1209. self.buttonRotate.isEnabled = false
  1210. if self.wbVC != nil && self.wbVC!.view.isDescendant(of: self.view){
  1211. self.wbVC!.view.removeFromSuperview()
  1212. self.buttonDecline.isHidden = false
  1213. self.buttonSpeaker.isHidden = false
  1214. self.buttonAddParticipant.isHidden = false
  1215. self.buttonRotate.isHidden = false
  1216. self.buttonMuted.isHidden = false
  1217. }
  1218. }
  1219. } else if self.dataPerson.count > 1 {
  1220. if self.dataPerson.firstIndex(where: {$0["f_pin"]!! == arrayMessage[1]}) == nil {
  1221. self.getDataProfile(fPin: String(arrayMessage[1]))
  1222. let i = self.dataPerson.count - 1
  1223. self.scrollRemoteView.addSubview(self.listRemoteViewFix[i])
  1224. self.listRemoteViewFix[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 160)
  1225. self.listRemoteViewFix[i].backgroundColor = .clear
  1226. self.listRemoteViewFix[i].makeRoundedView(radius: 8.0)
  1227. self.scrollRemoteView.addSubview(self.containerLabelName[i])
  1228. self.containerLabelName[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 30)
  1229. self.containerLabelName[i].backgroundColor = .orangeBNI.withAlphaComponent(0.5)
  1230. self.containerLabelName[i].makeRoundedView(radius: 8.0)
  1231. if arrayMessage[5] == "2" {
  1232. self.listRemoteViewFix[i].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (CGFloat.pi)/2)
  1233. } else {
  1234. self.listRemoteViewFix[i].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (-CGFloat.pi)/2)
  1235. }
  1236. let pictureImage = self.dataPerson[self.dataPerson.count - 1]["picture"] ?? ""
  1237. let namePerson = self.dataPerson[self.dataPerson.count - 1]["name"] ?? ""
  1238. if (!pictureImage!.isEmpty) {
  1239. self.listRemoteViewFix[i].setImage(name: pictureImage!)
  1240. self.listRemoteViewFix[i].contentMode = .scaleAspectFill
  1241. } else {
  1242. self.listRemoteViewFix[i].image = UIImage(systemName: "person")
  1243. self.listRemoteViewFix[i].backgroundColor = UIColor.systemGray6
  1244. self.listRemoteViewFix[i].contentMode = .scaleAspectFit
  1245. }
  1246. self.scrollRemoteView.contentSize.height = CGFloat(170 * (i + 1))
  1247. let labelName = UILabel()
  1248. self.containerLabelName[i].addSubview(labelName)
  1249. labelName.anchor(left: self.containerLabelName[i].leftAnchor, right: self.containerLabelName[i].rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: self.containerLabelName[i].centerXAnchor, centerY: self.containerLabelName[i].centerYAnchor)
  1250. labelName.text = namePerson
  1251. labelName.textAlignment = .center
  1252. labelName.textColor = .white
  1253. }
  1254. }
  1255. if self.users.count >= 1, let user = User.getData(pin: String(arrayMessage[1])), !self.users.contains(user) {
  1256. self.users.append(user)
  1257. }
  1258. if arrayMessage[5] == "2" && self.dataPerson.count == 1 {
  1259. DispatchQueue.main.async {
  1260. self.zoomView.transform = CGAffineTransform.init(scaleX: -1.9, y: 2.2).rotated(by: (CGFloat.pi)/2)
  1261. self.zoomView.contentMode = .scaleAspectFit
  1262. }
  1263. }
  1264. else if self.dataPerson.count == 1 {
  1265. DispatchQueue.main.async {
  1266. self.zoomView.transform = CGAffineTransform.init(scaleX: 1.9, y: 2.2).rotated(by: (-CGFloat.pi)/2)
  1267. self.zoomView.contentMode = .scaleAspectFit
  1268. }
  1269. } else if self.dataPerson.count > 1 {
  1270. DispatchQueue.main.async {
  1271. for i in 0..<self.dataPerson.count {
  1272. self.listRemoteViewFix[i].image = nil
  1273. if self.dataPerson[i]["user_type"] == "2" || arrayMessage[5] == "2" {
  1274. self.listRemoteViewFix[i].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (CGFloat.pi)/2)
  1275. } else {
  1276. self.listRemoteViewFix[i].transform = CGAffineTransform.init(scaleX: 1.4, y: 1.3).rotated(by: (-CGFloat.pi)/2)
  1277. }
  1278. }
  1279. }
  1280. }
  1281. }
  1282. DispatchQueue.main.async {
  1283. if self.isInisiator && self.name.isDescendant(of: self.view) {
  1284. self.didTapAcceptCallButton()
  1285. }
  1286. let indexPerson = self.dataPerson.firstIndex(where: {$0["f_pin"]!! == arrayMessage[1]})
  1287. if indexPerson != nil {
  1288. self.dataPerson[indexPerson!]["user_type"] = String(arrayMessage[5])
  1289. }
  1290. }
  1291. } else if (state == Nexilis.VIDEO_CALL_END || state == Nexilis.AUDIO_CALL_END) {
  1292. DispatchQueue.main.async {
  1293. Nexilis.stopRingtoneCall()
  1294. Nexilis.stopRingbacktoneCall()
  1295. }
  1296. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1297. if !onGoingCC.isEmpty {
  1298. let requester = onGoingCC.components(separatedBy: ",")[0]
  1299. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  1300. if arrayMessage[0] == requester || arrayMessage[0] == officer {
  1301. DispatchQueue.main.async {
  1302. let controller = self.presentedViewController
  1303. if controller != nil {
  1304. controller!.dismiss(animated: true)
  1305. }
  1306. if !self.showNotifCCEnd{
  1307. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  1308. imageView.tintColor = .white
  1309. let banner = FloatingNotificationBanner(title: "Call Center Session has ended".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
  1310. banner.show()
  1311. self.showNotifCCEnd = true
  1312. }
  1313. if self.stackViewToolbar.isDescendant(of: self.view){
  1314. self.stackViewToolbar.removeFromSuperview()
  1315. }
  1316. if self.stackViewToolbar2.isDescendant(of: self.view){
  1317. self.stackViewToolbar2.removeFromSuperview()
  1318. }
  1319. if self.buttonWB.isDescendant(of: self.view){
  1320. self.buttonWB.removeFromSuperview()
  1321. }
  1322. if self.buttonChat.isDescendant(of: self.view){
  1323. self.buttonChat.removeFromSuperview()
  1324. }
  1325. if self.buttonDecline.isDescendant(of: self.view) {
  1326. self.buttonDecline.removeFromSuperview()
  1327. }
  1328. if self.buttonAccept.isDescendant(of: self.view) {
  1329. self.buttonAccept.removeFromSuperview()
  1330. }
  1331. if self.buttonRotate.isDescendant(of: self.view) {
  1332. self.buttonRotate.removeFromSuperview()
  1333. }
  1334. if self.buttonMuted.isDescendant(of: self.view) {
  1335. self.buttonMuted.removeFromSuperview()
  1336. }
  1337. if self.wbVC != nil{
  1338. self.wbVC!.close?()
  1339. }
  1340. self.wbTimer.invalidate()
  1341. _ = Nexilis.getWhiteboardDelegate()?.terminate()
  1342. }
  1343. QmeraVideoViewController.isLoop = false
  1344. DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
  1345. self.endAllCall()
  1346. self.dismiss(animated: true, completion: nil)
  1347. }
  1348. return
  1349. }
  1350. }
  1351. DispatchQueue.main.async {
  1352. if (self.dataPerson.count == 1) {
  1353. var longCall = "0"
  1354. if self.vcTimer.isValid {
  1355. longCall = self.labelTimerVC.text ?? ""
  1356. }
  1357. self.makeStateCall()
  1358. // if self.labelIncomingOutgoing.isDescendant(of: self.view) {
  1359. // self.labelIncomingOutgoing.text = "Video call is over".localized()
  1360. // }
  1361. if self.stackViewToolbar.isDescendant(of: self.view){
  1362. self.stackViewToolbar.removeFromSuperview()
  1363. }
  1364. if self.stackViewToolbar2.isDescendant(of: self.view){
  1365. self.stackViewToolbar2.removeFromSuperview()
  1366. }
  1367. if self.buttonWB.isDescendant(of: self.view){
  1368. self.buttonWB.removeFromSuperview()
  1369. }
  1370. if self.buttonChat.isDescendant(of: self.view){
  1371. self.buttonChat.removeFromSuperview()
  1372. }
  1373. if self.buttonDecline.isDescendant(of: self.view) {
  1374. self.buttonDecline.removeFromSuperview()
  1375. }
  1376. if self.buttonAccept.isDescendant(of: self.view) {
  1377. self.buttonAccept.removeFromSuperview()
  1378. }
  1379. if self.buttonRotate.isDescendant(of: self.view) {
  1380. self.buttonRotate.removeFromSuperview()
  1381. }
  1382. if self.buttonMuted.isDescendant(of: self.view) {
  1383. self.buttonMuted.removeFromSuperview()
  1384. }
  1385. if self.wbVC != nil{
  1386. self.wbVC!.close?()
  1387. }
  1388. self.wbTimer.invalidate()
  1389. self.vcTimer.invalidate()
  1390. // self.labelTimerVC.text = "Video call is over".localized()
  1391. _ = Nexilis.getWhiteboardDelegate()?.terminate()
  1392. let controller = self.presentedViewController
  1393. if controller != nil {
  1394. controller!.dismiss(animated: true)
  1395. }
  1396. QmeraVideoViewController.isLoop = false
  1397. DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
  1398. self.endAllCall()
  1399. if self.isInisiator && !self.isPresent {
  1400. self.navigationController?.popViewController(animated: true)
  1401. } else {
  1402. self.dismiss(animated: true, completion: nil)
  1403. }
  1404. }
  1405. } else {
  1406. let indexPerson = self.dataPerson.firstIndex(where: {$0["f_pin"]!! == arrayMessage[0]})
  1407. if indexPerson != nil {
  1408. if (self.dataPerson.count == 2) {
  1409. self.containerLabelName.forEach({ $0.subviews.forEach({ $0.removeFromSuperview() }) })
  1410. self.scrollRemoteView.subviews.forEach({ $0.removeFromSuperview() })
  1411. if !self.buttonWB.isEnabled {
  1412. self.buttonWB.isEnabled = true
  1413. }
  1414. if !self.buttonRotate.isEnabled {
  1415. self.buttonRotate.isEnabled = true
  1416. }
  1417. } else {
  1418. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1419. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1420. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1421. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1422. if indexPerson! + 1 <= self.listRemoteViewFix.count {
  1423. let iLoop = (self.listRemoteViewFix.count - 1) - (indexPerson! + 1)
  1424. if iLoop >= 0 {
  1425. for i in 0...iLoop {
  1426. let viewAfterRemote = self.listRemoteViewFix[(indexPerson! + i) + 1]
  1427. let viewAfterName = self.containerLabelName[(indexPerson! + i) + 1]
  1428. viewAfterRemote.frame.origin.y = viewAfterRemote.frame.origin.y - 170
  1429. viewAfterName.frame.origin.y = viewAfterName.frame.origin.y - 170
  1430. UIView.animate(withDuration: 0.35, animations: {
  1431. self.scrollRemoteView.layoutIfNeeded()
  1432. })
  1433. }
  1434. }
  1435. }
  1436. }
  1437. self.dataPerson.remove(at: indexPerson!)
  1438. }
  1439. if !onGoingCC.isEmpty {
  1440. if let pin = arrayMessage.first, let index = self.users.firstIndex(of: User(pin: String(pin))) {
  1441. self.users.remove(at: index)
  1442. }
  1443. }
  1444. if self.dataPerson.count == 1 {
  1445. self.transformZoomAfterNewUserMore2 = false
  1446. self.zoomView.transform = CGAffineTransform.init(scaleX: 1.9, y: 2.2).rotated(by: (CGFloat.pi)/2)
  1447. }
  1448. }
  1449. }
  1450. } else if (state == Nexilis.OFFLINE) {
  1451. DispatchQueue.main.async {
  1452. Nexilis.stopRingtoneCall()
  1453. Nexilis.stopRingbacktoneCall()
  1454. }
  1455. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1456. DispatchQueue.main.async {
  1457. if (self.dataPerson.count == 1) {
  1458. if self.labelIncomingOutgoing.isDescendant(of: self.view) {
  1459. self.labelIncomingOutgoing.text = "Offline".localized()
  1460. }
  1461. if self.buttonDecline.isDescendant(of: self.view) {
  1462. self.buttonDecline.removeFromSuperview()
  1463. }
  1464. if self.buttonAccept.isDescendant(of: self.view) {
  1465. self.buttonAccept.removeFromSuperview()
  1466. }
  1467. } else {
  1468. let indexPerson = self.dataPerson.firstIndex(where: {$0["f_pin"]!! == arrayMessage[0]})
  1469. if indexPerson != nil {
  1470. if (self.dataPerson.count == 2) {
  1471. self.containerLabelName.forEach({ $0.subviews.forEach({ $0.removeFromSuperview() }) })
  1472. self.scrollRemoteView.subviews.forEach({ $0.removeFromSuperview() })
  1473. if !self.buttonWB.isEnabled {
  1474. self.buttonWB.isEnabled = true
  1475. }
  1476. if !self.buttonRotate.isEnabled {
  1477. self.buttonRotate.isEnabled = true
  1478. }
  1479. } else {
  1480. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1481. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1482. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1483. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1484. if indexPerson! + 1 <= self.listRemoteViewFix.count {
  1485. let viewAfterRemote = self.listRemoteViewFix[indexPerson! + 1]
  1486. let viewAfterName = self.containerLabelName[indexPerson! + 1]
  1487. viewAfterRemote.frame.origin.y = viewAfterRemote.frame.origin.y - 170
  1488. viewAfterName.frame.origin.y = viewAfterName.frame.origin.y - 170
  1489. UIView.animate(withDuration: 0.35, animations: {
  1490. self.scrollRemoteView.layoutIfNeeded()
  1491. })
  1492. }
  1493. }
  1494. }
  1495. if !onGoingCC.isEmpty {
  1496. if let pin = arrayMessage.first, let index = self.users.firstIndex(of: User(pin: String(pin))) {
  1497. self.users.remove(at: index)
  1498. if !onGoingCC.isEmpty && self.users.count != 0 {
  1499. DispatchQueue.main.async {
  1500. var members = ""
  1501. for user in self.users {
  1502. if members.isEmpty {
  1503. members = "\(user.pin)"
  1504. } else {
  1505. members = ",\(user.pin)"
  1506. }
  1507. }
  1508. SecureUserDefaults.shared.set("\(members)", forKey: "membersCC")
  1509. }
  1510. }
  1511. }
  1512. }
  1513. self.dataPerson.remove(at: indexPerson!)
  1514. }
  1515. }
  1516. if (self.dataPerson.count == 1) {
  1517. DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
  1518. self.endAllCall()
  1519. if self.isInisiator && onGoingCC.isEmpty && !self.isPresent {
  1520. self.navigationController?.popViewController(animated: true)
  1521. } else {
  1522. self.dismiss(animated: true, completion: nil)
  1523. }
  1524. }
  1525. }
  1526. } else if (state == Nexilis.BUSY) {
  1527. DispatchQueue.main.async {
  1528. Nexilis.stopRingtoneCall()
  1529. Nexilis.stopRingbacktoneCall()
  1530. }
  1531. let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
  1532. DispatchQueue.main.async { [self] in
  1533. if (self.dataPerson.count == 1) {
  1534. self.makeStateCall()
  1535. if self.labelIncomingOutgoing.isDescendant(of: self.view) {
  1536. self.labelIncomingOutgoing.text = "Busy".localized()
  1537. }
  1538. if self.buttonDecline.isDescendant(of: self.view) {
  1539. self.buttonDecline.removeFromSuperview()
  1540. }
  1541. if self.buttonAccept.isDescendant(of: self.view) {
  1542. self.buttonAccept.removeFromSuperview()
  1543. }
  1544. } else {
  1545. let indexPerson = self.dataPerson.firstIndex(where: {$0["f_pin"]!! == arrayMessage[0]})
  1546. if indexPerson != nil {
  1547. if (self.dataPerson.count == 2) {
  1548. self.containerLabelName.forEach({ $0.subviews.forEach({ $0.removeFromSuperview() }) })
  1549. self.scrollRemoteView.subviews.forEach({ $0.removeFromSuperview() })
  1550. if !self.buttonWB.isEnabled {
  1551. self.buttonWB.isEnabled = true
  1552. }
  1553. if !self.buttonRotate.isEnabled {
  1554. self.buttonRotate.isEnabled = true
  1555. }
  1556. } else {
  1557. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1558. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1559. self.containerLabelName[indexPerson! + indexPerson!].subviews.forEach({ $0.removeFromSuperview() })
  1560. self.scrollRemoteView.subviews[indexPerson! + indexPerson!].removeFromSuperview()
  1561. if indexPerson! + 1 <= self.listRemoteViewFix.count {
  1562. let viewAfterRemote = self.listRemoteViewFix[indexPerson! + 1]
  1563. let viewAfterName = self.containerLabelName[indexPerson! + 1]
  1564. viewAfterRemote.frame.origin.y = viewAfterRemote.frame.origin.y - 170
  1565. viewAfterName.frame.origin.y = viewAfterName.frame.origin.y - 170
  1566. UIView.animate(withDuration: 0.35, animations: {
  1567. self.scrollRemoteView.layoutIfNeeded()
  1568. })
  1569. }
  1570. }
  1571. }
  1572. if !onGoingCC.isEmpty {
  1573. if let pin = arrayMessage.first, let index = self.users.firstIndex(of: User(pin: String(pin))) {
  1574. self.users.remove(at: index)
  1575. if !onGoingCC.isEmpty && users.count != 0 {
  1576. DispatchQueue.main.async {
  1577. var members = ""
  1578. for user in self.users {
  1579. if members.isEmpty {
  1580. members = "\(user.pin)"
  1581. } else {
  1582. members = ",\(user.pin)"
  1583. }
  1584. }
  1585. SecureUserDefaults.shared.set("\(members)", forKey: "membersCC")
  1586. }
  1587. }
  1588. }
  1589. }
  1590. self.dataPerson.remove(at: indexPerson!)
  1591. }
  1592. }
  1593. if (self.dataPerson.count == 1) {
  1594. DispatchQueue.main.async {
  1595. Nexilis.playBusyCall()
  1596. }
  1597. DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
  1598. Nexilis.stopBusyCall()
  1599. self.endAllCall()
  1600. if self.isInisiator && onGoingCC.isEmpty && !self.isPresent {
  1601. self.navigationController?.popViewController(animated: true)
  1602. } else {
  1603. self.dismiss(animated: true, completion: nil)
  1604. }
  1605. }
  1606. }
  1607. } else if (state == Nexilis.VIDEO_CALL_RINGING) {
  1608. DispatchQueue.main.async {
  1609. if (self.dataPerson.count > 1) {
  1610. if (self.dataPerson.count == 2) {
  1611. for i in 0...1{
  1612. self.scrollRemoteView.addSubview(self.listRemoteViewFix[i])
  1613. self.listRemoteViewFix[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 160)
  1614. self.listRemoteViewFix[i].backgroundColor = .clear
  1615. self.listRemoteViewFix[i].makeRoundedView(radius: 8.0)
  1616. self.scrollRemoteView.addSubview(self.containerLabelName[i])
  1617. self.containerLabelName[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 30)
  1618. self.containerLabelName[i].backgroundColor = .orangeBNI.withAlphaComponent(0.5)
  1619. self.containerLabelName[i].makeRoundedView(radius: 8.0)
  1620. let pictureImage = self.dataPerson[i]["picture"] ?? ""
  1621. let namePerson = self.dataPerson[i]["name"] ?? ""
  1622. if (!pictureImage!.isEmpty) {
  1623. self.listRemoteViewFix[i].setImage(name: pictureImage!)
  1624. self.listRemoteViewFix[i].contentMode = .scaleAspectFill
  1625. } else {
  1626. self.listRemoteViewFix[i].image = UIImage(systemName: "person")
  1627. self.listRemoteViewFix[i].backgroundColor = UIColor.systemGray6
  1628. self.listRemoteViewFix[i].contentMode = .scaleAspectFit
  1629. }
  1630. let labelName = UILabel()
  1631. self.containerLabelName[i].addSubview(labelName)
  1632. labelName.anchor(left: self.containerLabelName[i].leftAnchor, right: self.containerLabelName[i].rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: self.containerLabelName[i].centerXAnchor, centerY: self.containerLabelName[i].centerYAnchor)
  1633. labelName.text = namePerson
  1634. labelName.textAlignment = .center
  1635. labelName.textColor = .white
  1636. }
  1637. self.scrollRemoteView.contentSize.height = CGFloat(170 * 2)
  1638. } else {
  1639. let i = self.dataPerson.count - 1
  1640. self.scrollRemoteView.addSubview(self.listRemoteViewFix[i])
  1641. self.listRemoteViewFix[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 160)
  1642. self.listRemoteViewFix[i].backgroundColor = .clear
  1643. self.listRemoteViewFix[i].makeRoundedView(radius: 8.0)
  1644. self.scrollRemoteView.addSubview(self.containerLabelName[i])
  1645. self.containerLabelName[i].frame = CGRect(x: 0, y: 170 * i, width: 120, height: 30)
  1646. self.containerLabelName[i].backgroundColor = .orangeBNI.withAlphaComponent(0.5)
  1647. self.containerLabelName[i].makeRoundedView(radius: 8.0)
  1648. let pictureImage = self.dataPerson[self.dataPerson.count - 1]["picture"] ?? ""
  1649. let namePerson = self.dataPerson[self.dataPerson.count - 1]["name"] ?? ""
  1650. if (!pictureImage!.isEmpty) {
  1651. self.listRemoteViewFix[i].setImage(name: pictureImage!)
  1652. self.listRemoteViewFix[i].contentMode = .scaleAspectFill
  1653. } else {
  1654. self.listRemoteViewFix[i].image = UIImage(systemName: "person")
  1655. self.listRemoteViewFix[i].backgroundColor = UIColor.systemGray6
  1656. self.listRemoteViewFix[i].contentMode = .scaleAspectFit
  1657. }
  1658. self.scrollRemoteView.contentSize.height = CGFloat(170 * (i + 1))
  1659. let labelName = UILabel()
  1660. self.containerLabelName[i].addSubview(labelName)
  1661. labelName.anchor(left: self.containerLabelName[i].leftAnchor, right: self.containerLabelName[i].rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: self.containerLabelName[i].centerXAnchor, centerY: self.containerLabelName[i].centerYAnchor)
  1662. labelName.text = namePerson
  1663. labelName.textAlignment = .center
  1664. labelName.textColor = .white
  1665. }
  1666. if self.buttonWB.isEnabled {
  1667. self.buttonWB.isEnabled = false
  1668. }
  1669. if self.buttonRotate.isEnabled {
  1670. self.buttonRotate.isEnabled = false
  1671. }
  1672. }
  1673. }
  1674. }
  1675. }
  1676. }
  1677. extension QmeraVideoViewController : WhiteboardReceiver {
  1678. func incomingWB(roomId: String) {
  1679. //print("incoming wb")
  1680. self.wbTimer.invalidate()
  1681. if(wbRoomId.isEmpty){
  1682. //print("wbroom empty")
  1683. DispatchQueue.main.async {
  1684. self.wbTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.runTimer), userInfo: nil, repeats: true)
  1685. }
  1686. let me = User.getMyPin()!
  1687. var destString = ""
  1688. for d in dataPerson{
  1689. if d["f_pin"]!! == roomId.components(separatedBy: "wbvc")[0] {
  1690. continue
  1691. }
  1692. if destString.isEmpty{
  1693. destString = d["f_pin"]!!
  1694. } else {
  1695. destString = destString + ",\(d["f_pin"]!!)"
  1696. }
  1697. }
  1698. if destString.isEmpty {
  1699. SecureUserDefaults.shared.set("\(roomId.components(separatedBy: "wbvc")[0]),\(me)", forKey: "wb_vc")
  1700. } else {
  1701. SecureUserDefaults.shared.set("\(roomId.components(separatedBy: "wbvc")[0]),\(me),\(destString)", forKey: "wb_vc")
  1702. }
  1703. wbRoomId = roomId
  1704. }
  1705. }
  1706. func cancel(roomId: String) {
  1707. DispatchQueue.main.async {
  1708. self.wbTimer.invalidate()
  1709. self.wbBlink = false
  1710. self.buttonWB.backgroundColor = .lightGray
  1711. self.buttonWB.setNeedsDisplay()
  1712. }
  1713. wbRoomId = ""
  1714. }
  1715. @objc func runTimer(){
  1716. DispatchQueue.main.async {
  1717. if !self.buttonWB.isEnabled {
  1718. return
  1719. }
  1720. self.wbBlink = !self.wbBlink
  1721. if(self.wbBlink){
  1722. self.buttonWB.backgroundColor = .green
  1723. }
  1724. else {
  1725. //print("set wb blink off")
  1726. self.buttonWB.backgroundColor = .lightGray
  1727. }
  1728. self.buttonWB.setNeedsDisplay()
  1729. }
  1730. }
  1731. }