QmeraAudioViewController.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. //
  2. // OutgoingViewController.swift
  3. // Qmera
  4. //
  5. // Created by Yayan Dwi on 07/10/21.
  6. //
  7. import UIKit
  8. import AVFoundation
  9. import nuSDKService
  10. import NotificationBannerSwift
  11. class QmeraAudioViewController: UIViewController {
  12. let buttonSize: CGFloat = 70
  13. lazy var data: String = "" {
  14. didSet {
  15. getUserData { user in
  16. self.user = user
  17. }
  18. }
  19. }
  20. var user: User?
  21. var isAddCall = ""
  22. private var isEndByMe = false
  23. private var users: [User] = [] {
  24. didSet {
  25. DispatchQueue.main.async {
  26. if oldValue.count > self.users.count { // remove
  27. let remove = oldValue.filter { !self.users.contains($0) }
  28. remove.forEach { user in
  29. if let subviews = self.profiles.subviews as? [ProfileView] {
  30. subviews.forEach { p in
  31. if p.user == user {
  32. self.profiles.removeArrangeSubview(view: p)
  33. }
  34. }
  35. }
  36. }
  37. } else {
  38. if let user = self.users.last {
  39. let profile = ProfileView(image: UIImage(systemName: "person.circle.fill"))
  40. profile.user = user
  41. self.profiles.addArrangedSubview(view: profile)
  42. }
  43. }
  44. self.name.text = self.users.map { $0.fullName }.joined(separator: ", ")
  45. }
  46. }
  47. }
  48. var isOutgoing: Bool = true
  49. var isOnGoing: Bool = false
  50. private var timer: Timer?
  51. private var firstCall: Bool = true
  52. private var isSpeaker: Bool = false
  53. let status: UILabel = {
  54. let label = UILabel()
  55. label.text = "Calling..."
  56. label.font = UIFont.systemFont(ofSize: 14)
  57. label.textColor = .white
  58. label.textAlignment = .center
  59. return label
  60. }()
  61. let profiles: GroupView = {
  62. let groupView = GroupView()
  63. groupView.spacing = 50
  64. groupView.maxUser = 3
  65. return groupView
  66. }()
  67. let name: UILabel = {
  68. let label = UILabel()
  69. label.text = "uwitan"
  70. label.font = UIFont.systemFont(ofSize: 14)
  71. label.textColor = .white
  72. label.textAlignment = .center
  73. return label
  74. }()
  75. let end: UIButton = {
  76. let button = UIButton()
  77. button.setImage(UIImage(systemName: "phone.down"), for: .normal)
  78. button.imageView?.contentMode = .scaleAspectFit
  79. button.imageView?.tintColor = .white
  80. button.setBackgroundColor(.red, for: .normal)
  81. button.setBackgroundColor(.white, for: .highlighted)
  82. button.contentVerticalAlignment = .fill
  83. button.contentHorizontalAlignment = .fill
  84. button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  85. return button
  86. }()
  87. let reject: UIButton = {
  88. let button = UIButton()
  89. let image = UIImage(systemName: "xmark")
  90. button.setImage(image, for: .normal)
  91. let selectedImage = image?.withTintColor(.mainColor)
  92. button.setImage(selectedImage, for: .selected)
  93. button.imageView?.contentMode = .scaleAspectFit
  94. button.imageView?.tintColor = .white
  95. button.setBackgroundColor(.red, for: .normal)
  96. button.setBackgroundColor(.white, for: .highlighted)
  97. button.contentVerticalAlignment = .fill
  98. button.contentHorizontalAlignment = .fill
  99. button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  100. return button
  101. }()
  102. let accept: UIButton = {
  103. let button = UIButton()
  104. let image = UIImage(systemName: "checkmark")
  105. button.setImage(image, for: .normal)
  106. button.imageView?.contentMode = .scaleAspectFit
  107. button.imageView?.tintColor = .white
  108. button.setBackgroundColor(.greenColor, for: .normal)
  109. button.setBackgroundColor(.white, for: .highlighted)
  110. button.contentVerticalAlignment = .fill
  111. button.contentHorizontalAlignment = .fill
  112. button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  113. return button
  114. }()
  115. let invite: UIButton = {
  116. let button = UIButton()
  117. let image = UIImage(systemName: "person.badge.plus")
  118. button.setImage(image, for: .normal)
  119. button.imageView?.contentMode = .scaleAspectFit
  120. button.imageView?.tintColor = .mainColor
  121. button.setBackgroundColor(.white, for: .normal)
  122. button.setBackgroundColor(.mainColor, for: .highlighted)
  123. button.contentVerticalAlignment = .fill
  124. button.contentHorizontalAlignment = .fill
  125. button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  126. return button
  127. }()
  128. let speaker: UIButton = {
  129. let button = UIButton()
  130. button.setImage(UIImage(systemName: "speaker.slash")?.withTintColor(.mainColor, renderingMode: .alwaysOriginal), for: .normal)
  131. button.setImage(UIImage(systemName: "speaker.wave.3")?.withTintColor(.white, renderingMode: .alwaysOriginal), for: .selected)
  132. button.imageView?.contentMode = .scaleAspectFit
  133. button.setBackgroundColor(.white, for: .normal)
  134. button.setBackgroundColor(.mainColor, for: .highlighted)
  135. button.setBackgroundColor(.mainColor, for: .selected)
  136. button.contentVerticalAlignment = .fill
  137. button.contentHorizontalAlignment = .fill
  138. button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  139. return button
  140. }()
  141. let stack: UIStackView = {
  142. let stackView = UIStackView()
  143. stackView.axis = .horizontal
  144. stackView.distribution = .fillEqually
  145. return stackView
  146. }()
  147. let poweredByView: UIStackView = {
  148. let stackView = UIStackView()
  149. stackView.axis = .horizontal
  150. stackView.spacing = 5
  151. return stackView
  152. }()
  153. let poweredByLabel: UILabel = {
  154. let label = UILabel()
  155. label.text = "Powered by Nexilis"
  156. return label
  157. }()
  158. let qmeraLogo: UIButton = {
  159. let image = UIImage(named: "Q-Button-PNG", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  160. let button = UIButton()
  161. button.setImage(image, for: .normal)
  162. button.imageView?.contentMode = .scaleAspectFit
  163. button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
  164. button.contentVerticalAlignment = .fill
  165. button.contentHorizontalAlignment = .fill
  166. button.frame.size.width = 30
  167. button.frame.size.height = 30
  168. return button
  169. }()
  170. let nexilisLogo: UIButton = {
  171. let image = UIImage(named: "pb_powered_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
  172. let button = UIButton()
  173. button.setImage(image, for: .normal)
  174. button.imageView?.contentMode = .scaleAspectFit
  175. button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
  176. button.contentVerticalAlignment = .fill
  177. button.contentHorizontalAlignment = .fill
  178. button.frame.size.width = 30
  179. button.frame.size.height = 30
  180. return button
  181. }()
  182. override func viewWillDisappear(_ animated: Bool) {
  183. UIDevice.current.isProximityMonitoringEnabled = false
  184. NotificationCenter.default.removeObserver(self)
  185. }
  186. deinit {
  187. UIDevice.current.isProximityMonitoringEnabled = false
  188. NotificationCenter.default.removeObserver(self)
  189. }
  190. override func viewDidAppear(_ animated: Bool) {
  191. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onShowAC"), object: nil, userInfo: nil)
  192. }
  193. override func viewDidLoad() {
  194. super.viewDidLoad()
  195. let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
  196. effectView.frame = view.frame
  197. view.insertSubview(effectView, at: 0)
  198. view.addSubview(status)
  199. view.addSubview(profiles)
  200. view.addSubview(name)
  201. status.anchor(left: view.leftAnchor, bottom: profiles.topAnchor, right: view.rightAnchor, paddingBottom: 30, centerX: view.centerXAnchor)
  202. profiles.anchor(centerX: view.centerXAnchor, centerY: view.centerYAnchor, width: 150, height: 150)
  203. name.anchor(top: profiles.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 5, paddingLeft: 20, paddingRight: 20, centerX: view.centerXAnchor)
  204. definesPresentationContext = true
  205. NotificationCenter.default.addObserver(self, selector: #selector(onStatusCall(_:)), name: NSNotification.Name(rawValue: "onStatusCall"), object: nil)
  206. NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil)
  207. if let u = self.user {
  208. self.users.append(u)
  209. if isOutgoing {
  210. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  211. if onGoingCC.isEmpty {
  212. Nexilis.shared.callManager.startCall(handle: u.pin)
  213. } else {
  214. API.initiateCCall(sParty: u.pin)
  215. }
  216. }
  217. }
  218. if isOutgoing {
  219. outgoingView()
  220. } else if isOnGoing {
  221. ongoingView()
  222. } else {
  223. incomingView()
  224. }
  225. UIDevice.current.isProximityMonitoringEnabled = true
  226. }
  227. override func viewWillLayoutSubviews() {
  228. super.viewWillLayoutSubviews()
  229. end.circle()
  230. reject.circle()
  231. accept.circle()
  232. invite.circle()
  233. speaker.circle()
  234. }
  235. private func getUserData(completion: @escaping (User?) -> ()) {
  236. if let user = self.user {
  237. completion(user)
  238. return
  239. }
  240. var user: User?
  241. DispatchQueue.global().async {
  242. Database.shared.database?.inTransaction({ fmdb, rollback in
  243. if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select f_pin, first_name, last_name, image_id from BUDDY where f_pin = '\(self.data)'"), cursor.next() {
  244. user = User(pin: cursor.string(forColumnIndex: 0) ?? "",
  245. firstName: cursor.string(forColumnIndex: 1) ?? "",
  246. lastName: cursor.string(forColumnIndex: 2) ?? "",
  247. thumb: cursor.string(forColumnIndex: 3) ?? "")
  248. cursor.close()
  249. }
  250. })
  251. }
  252. completion(user)
  253. }
  254. private func outgoingView() {
  255. view.addSubview(end)
  256. end.anchor(bottom: view.bottomAnchor, paddingBottom: 60, centerX: view.centerXAnchor, width: buttonSize, height: buttonSize)
  257. end.addTarget(self, action: #selector(didPressEnd(sender:)), for: .touchUpInside)
  258. }
  259. private func incomingView() {
  260. status.text = "Incoming..."
  261. stack.spacing = buttonSize
  262. view.addSubview(stack)
  263. stack.anchor(bottom: view.bottomAnchor, paddingBottom: 60, centerX: view.centerXAnchor, width: buttonSize * 3, height: buttonSize)
  264. stack.addArrangedSubview(reject)
  265. stack.addArrangedSubview(accept)
  266. reject.addTarget(self, action: #selector(didReject(sender:)), for: .touchUpInside)
  267. accept.addTarget(self, action: #selector(didAccept(sender:)), for: .touchUpInside)
  268. }
  269. private func ongoingView() {
  270. status.text = "Connecting..."
  271. stack.spacing = buttonSize / 2
  272. view.addSubview(stack)
  273. stack.anchor(bottom: view.bottomAnchor, paddingBottom: 60, centerX: view.centerXAnchor, width: buttonSize * 4, height: buttonSize)
  274. stack.addArrangedSubview(invite)
  275. stack.addArrangedSubview(end)
  276. stack.addArrangedSubview(speaker)
  277. invite.addTarget(self, action: #selector(didInvite(sender:)), for: .touchUpInside)
  278. end.addTarget(self, action: #selector(didPressEnd(sender:)), for: .touchUpInside)
  279. speaker.addTarget(self, action: #selector(didSpeaker(sender:)), for: .touchUpInside)
  280. self.view.addSubview(poweredByView)
  281. self.poweredByView.translatesAutoresizingMaskIntoConstraints = false
  282. let constraintRightPowered = self.poweredByView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0)
  283. let constraintBottomPowered = self.poweredByView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0)
  284. NSLayoutConstraint.activate([
  285. constraintRightPowered,
  286. constraintBottomPowered,
  287. poweredByView.heightAnchor.constraint(equalToConstant: 40.0),
  288. nexilisLogo.widthAnchor.constraint(equalToConstant: 30.0),
  289. nexilisLogo.heightAnchor.constraint(equalToConstant: 30.0)
  290. ])
  291. poweredByView.addArrangedSubview(poweredByLabel)
  292. poweredByView.addArrangedSubview(nexilisLogo)
  293. }
  294. // MARK: - Action
  295. @objc func didSpeaker(sender: Any?) {
  296. isSpeaker = !isSpeaker
  297. speaker.isSelected = isSpeaker
  298. if isSpeaker {
  299. UIDevice.current.isProximityMonitoringEnabled = false
  300. } else {
  301. UIDevice.current.isProximityMonitoringEnabled = true
  302. }
  303. Nexilis.turnSpeakerOn(bSpeakerOn: isSpeaker)
  304. }
  305. @objc func didInvite(sender: Any?) {
  306. let controller = QmeraCallContactViewController()
  307. controller.isDismiss = { user in
  308. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  309. if !onGoingCC.isEmpty {
  310. DispatchQueue.global().async {
  311. _ = Nexilis.write(message: CoreMessage_TMessageBank.getCCRoomInvite(l_pin: user.pin, ticket_id: onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2], channel: "1"))
  312. }
  313. DispatchQueue.main.async {
  314. self.isAddCall = user.pin
  315. }
  316. } else {
  317. self.users.append(user)
  318. // Start Calling
  319. Nexilis.shared.callManager.startCall(handle: user.pin)
  320. }
  321. }
  322. controller.selectedUser.append(contentsOf: users)
  323. present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
  324. }
  325. @objc func didPressEnd(sender: Any?) {
  326. isEndByMe = true
  327. self.didEnd(sender: nil)
  328. }
  329. @objc func didEnd(sender: Any?) {
  330. poweredByView.isHidden = true
  331. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  332. if !onGoingCC.isEmpty {
  333. if sender != nil && sender is Bool {
  334. self.dismiss(animated: false, completion: nil)
  335. let requester = onGoingCC.components(separatedBy: ",")[0]
  336. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  337. let complaintId = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2]
  338. let startTimeCC = UserDefaults.standard.string(forKey: "startTimeCC") ?? ""
  339. DispatchQueue.global().async {
  340. if sender as! Bool == true {
  341. let date = "\(Date().currentTimeMillis())"
  342. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  343. do {
  344. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  345. "type" : "1",
  346. "title" : "Contact Center".localized(),
  347. "time" : startTimeCC,
  348. "f_pin" : officer,
  349. "data" : complaintId,
  350. "time_end" : date,
  351. "complaint_id" : complaintId,
  352. "members" : "",
  353. "requester": requester
  354. ], replace: true)
  355. } catch {
  356. rollback.pointee = true
  357. print(error)
  358. }
  359. })
  360. }
  361. UserDefaults.standard.removeObject(forKey: "onGoingCC")
  362. UserDefaults.standard.removeObject(forKey: "membersCC")
  363. UserDefaults.standard.removeObject(forKey: "startTimeCC")
  364. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  365. }
  366. return
  367. }
  368. let alert = UIAlertController(title: "Interaction with Call Center is in progress".localized(), message: "Are you sure you want to end the Call Center?".localized(), preferredStyle: .alert)
  369. alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
  370. alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {(_) in
  371. self.dismiss(animated: false, completion: nil)
  372. let requester = onGoingCC.components(separatedBy: ",")[0]
  373. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  374. let complaintId = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2]
  375. let idMe = UserDefaults.standard.string(forKey: "me")!
  376. let startTimeCC = UserDefaults.standard.string(forKey: "startTimeCC") ?? ""
  377. DispatchQueue.global().async {
  378. let date = "\(Date().currentTimeMillis())"
  379. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  380. do {
  381. _ = try Database.shared.insertRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", cvalues: [
  382. "type" : "1",
  383. "title" : "Contact Center".localized(),
  384. "time" : startTimeCC,
  385. "f_pin" : officer,
  386. "data" : complaintId,
  387. "time_end" : date,
  388. "complaint_id" : complaintId,
  389. "members" : "",
  390. "requester": requester
  391. ], replace: true)
  392. } catch {
  393. rollback.pointee = true
  394. print(error)
  395. }
  396. })
  397. if officer == idMe {
  398. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: requester))
  399. } else {
  400. if requester == idMe {
  401. _ = Nexilis.write(message: CoreMessage_TMessageBank.endCallCenter(complaint_id: complaintId, l_pin: officer))
  402. } else {
  403. _ = Nexilis.write(message: CoreMessage_TMessageBank.leaveCCRoomInvite(ticket_id: complaintId))
  404. }
  405. }
  406. UserDefaults.standard.removeObject(forKey: "onGoingCC")
  407. UserDefaults.standard.removeObject(forKey: "membersCC")
  408. UserDefaults.standard.removeObject(forKey: "startTimeCC")
  409. UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
  410. }
  411. if let user = self.user, let call = Nexilis.shared.callManager.call(with: user.pin) {
  412. Nexilis.shared.callManager.end(call: call)
  413. } else {
  414. API.terminateCall(sParty: nil)
  415. }
  416. }))
  417. self.present(alert, animated: true, completion: nil)
  418. } else {
  419. if isEndByMe {
  420. for i in 0..<Nexilis.shared.callManager.calls.count {
  421. Nexilis.shared.callManager.end(call: Nexilis.shared.callManager.calls[i])
  422. }
  423. } else {
  424. if let user = self.user, let call = Nexilis.shared.callManager.call(with: user.pin) {
  425. Nexilis.shared.callManager.end(call: call)
  426. } else {
  427. API.terminateCall(sParty: nil)
  428. }
  429. }
  430. dismiss(animated: false, completion: nil)
  431. }
  432. }
  433. @objc func didReject(sender: Any?) {
  434. didEnd(sender: sender)
  435. }
  436. @objc func didAccept(sender: Any?) {
  437. NSLayoutConstraint.deactivate(stack.constraints)
  438. stack.subviews.forEach { subview in
  439. subview.removeFromSuperview()
  440. }
  441. ongoingView()
  442. UIView.animate(withDuration: 0.3, animations: {
  443. self.view.layoutIfNeeded()
  444. })
  445. }
  446. // MARK: - Communication
  447. @objc func onReceiveMessage(notification: NSNotification) {
  448. DispatchQueue.main.async {
  449. let data:[AnyHashable : Any] = notification.userInfo!
  450. if let dataMessage = data["message"] as? TMessage {
  451. if (dataMessage.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER) {
  452. let data = dataMessage.getBody(key: CoreMessage_TMessageKey.DATA)
  453. if !data.isEmpty {
  454. if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
  455. var members = ""
  456. let idMe = UserDefaults.standard.string(forKey: "me")!
  457. for json in jsonArray {
  458. if "\(json)" != idMe {
  459. if members.isEmpty {
  460. members = "\(json)"
  461. } else {
  462. members += ",\(json)"
  463. }
  464. }
  465. }
  466. UserDefaults.standard.set(members, forKey: "inEditorPersonal")
  467. }
  468. }
  469. self.users.append(User.getData(pin: dataMessage.getPIN())!)
  470. // Start Calling
  471. if !self.isAddCall.isEmpty && self.isAddCall == dataMessage.getPIN() {
  472. // Nexilis.shared.callManager.startCall(handle: dataMessage.getPIN())
  473. API.initiateCCall(sParty: dataMessage.getPIN())
  474. }
  475. }
  476. }
  477. }
  478. }
  479. @objc func onStatusCall(_ notification: NSNotification) {
  480. if let data = notification.userInfo,
  481. let state = data["state"] as? Int,
  482. let message = data["message"] as? String
  483. {
  484. let arrayMessage = message.split(separator: ",")
  485. if state == 23 {
  486. if users.count == 1 {
  487. DispatchQueue.main.async {
  488. self.status.text = "Ringing..."
  489. }
  490. }
  491. } else if state == 22 {
  492. if users.count == 1 && firstCall {
  493. DispatchQueue.main.async {
  494. self.ongoingView()
  495. let connectDate = Date()
  496. self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
  497. let format = Utils.callDurationFormatter.string(from: Date().timeIntervalSince(connectDate))
  498. self.status.text = format
  499. }
  500. self.timer?.fire()
  501. self.firstCall = false
  502. }
  503. }
  504. if (!isOutgoing || !firstCall), users.count >= 1, let user = User.getData(pin: String(arrayMessage[1])), !users.contains(user) {
  505. self.users.append(user)
  506. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  507. if !onGoingCC.isEmpty {
  508. DispatchQueue.main.async {
  509. var members = ""
  510. for user in self.users {
  511. if members.isEmpty {
  512. members = "\(user.pin)"
  513. } else {
  514. members = ",\(user.pin)"
  515. }
  516. }
  517. UserDefaults.standard.set("\(members)", forKey: "membersCC")
  518. }
  519. }
  520. }
  521. } else if state == 28 {
  522. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  523. if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
  524. users.remove(at: index)
  525. if !onGoingCC.isEmpty && users.count != 0 {
  526. let requester = onGoingCC.components(separatedBy: ",")[0]
  527. let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
  528. if pin == requester || pin == officer {
  529. DispatchQueue.main.async {
  530. if self.viewIfLoaded?.window != nil {
  531. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  532. imageView.tintColor = .white
  533. 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)
  534. banner.show()
  535. }
  536. self.timer?.invalidate()
  537. self.timer = nil
  538. self.status.text = "Call Center Session has ended..."
  539. self.end.isEnabled = false
  540. }
  541. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  542. self.didEnd(sender: true)
  543. }
  544. return
  545. }
  546. } else if !onGoingCC.isEmpty && users.count == 0 {
  547. DispatchQueue.main.async {
  548. if self.viewIfLoaded?.window != nil {
  549. let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
  550. imageView.tintColor = .white
  551. 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)
  552. banner.show()
  553. }
  554. self.timer?.invalidate()
  555. self.timer = nil
  556. self.status.text = "Call Center Session has ended..."
  557. self.end.isEnabled = false
  558. }
  559. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  560. self.didEnd(sender: true)
  561. }
  562. return
  563. } else if users.count == 0 {
  564. DispatchQueue.main.async {
  565. self.timer?.invalidate()
  566. self.timer = nil
  567. self.status.text = "Audio Call Ended"
  568. self.end.isEnabled = false
  569. self.invite.isEnabled = false
  570. self.speaker.isEnabled = false
  571. let controller = self.presentedViewController
  572. if controller != nil {
  573. controller!.dismiss(animated: true)
  574. }
  575. }
  576. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  577. self.didEnd(sender: true)
  578. }
  579. return
  580. }
  581. }
  582. // if users.count == 0 {
  583. // DispatchQueue.main.async {
  584. // self.dismiss(animated: false, completion: nil)
  585. // }
  586. // }
  587. } else if state == -3 { // Offline
  588. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  589. if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
  590. users.remove(at: index)
  591. if !onGoingCC.isEmpty && users.count != 0 {
  592. DispatchQueue.main.async {
  593. var members = ""
  594. for user in self.users {
  595. if members.isEmpty {
  596. members = "\(user.pin)"
  597. } else {
  598. members = ",\(user.pin)"
  599. }
  600. }
  601. UserDefaults.standard.set("\(members)", forKey: "membersCC")
  602. }
  603. }
  604. }
  605. if users.count == 0 {
  606. DispatchQueue.main.async {
  607. self.status.text = "Offline..."
  608. self.end.isEnabled = false
  609. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  610. self.didEnd(sender: false)
  611. }
  612. }
  613. }
  614. } else if state == -4 { // Busy
  615. let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
  616. if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
  617. users.remove(at: index)
  618. if !onGoingCC.isEmpty && users.count != 0 {
  619. DispatchQueue.main.async {
  620. var members = ""
  621. for user in self.users {
  622. if members.isEmpty {
  623. members = "\(user.pin)"
  624. } else {
  625. members = ",\(user.pin)"
  626. }
  627. }
  628. UserDefaults.standard.set("\(members)", forKey: "membersCC")
  629. }
  630. }
  631. }
  632. if users.count == 0 {
  633. DispatchQueue.main.async {
  634. self.status.text = "Busy..."
  635. self.end.isEnabled = false
  636. DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
  637. self.didEnd(sender: false)
  638. }
  639. }
  640. }
  641. }
  642. }
  643. }
  644. }