FirstTabViewController.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. //
  2. // FirstTabViewController.swift
  3. // AppBuilder
  4. //
  5. // Created by Kevin Maulana on 29/03/22.
  6. //
  7. import UIKit
  8. import WebKit
  9. import NexilisLite
  10. import Speech
  11. import CommonCrypto
  12. class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKScriptMessageHandler, ImageVideoPickerDelegate {
  13. var webView: WKWebView!
  14. var address = ""
  15. private var lastContentOffset: CGFloat = 0
  16. var isAllowSpeech = false
  17. let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
  18. var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
  19. var recognitionTask : SFSpeechRecognitionTask?
  20. let audioEngine = AVAudioEngine()
  21. var alertController = LibAlertController()
  22. public static var forceRefresh = true
  23. public static var atFirstPage = true
  24. public static var showModal = false
  25. var indexImageVideoWv = 0
  26. var imageVideoPicker: ImageVideoPicker!
  27. var blockedCertificate = ""
  28. var allowedURLs = Set<String>()
  29. override func viewDidLoad() {
  30. super.viewDidLoad()
  31. self.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
  32. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(collapseDocked))
  33. tapGesture.cancelsTouchesInView = false
  34. tapGesture.delegate = self
  35. let configuration = WKWebViewConfiguration()
  36. configuration.allowsInlineMediaPlayback = true
  37. let customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1 \(Utils.getUserAgent())"
  38. let finalUserAgent = "\(customUserAgent)"
  39. configuration.applicationNameForUserAgent = finalUserAgent
  40. webView = WKWebView(frame: .zero, configuration: configuration)
  41. view.addSubview(webView)
  42. webView.translatesAutoresizingMaskIntoConstraints = false
  43. NSLayoutConstraint.activate([
  44. webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
  45. webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  46. webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  47. webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
  48. ])
  49. webView.scrollView.addGestureRecognizer(tapGesture)
  50. let refreshControl = UIRefreshControl()
  51. refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
  52. webView.scrollView.addSubview(refreshControl)
  53. webView.scrollView.delegate = self
  54. webView.navigationDelegate = self
  55. webView.allowsBackForwardNavigationGestures = true
  56. let contentController = self.webView.configuration.userContentController
  57. contentController.add(self, name: "checkProfile")
  58. contentController.add(self, name: "setIsProductModalOpen")
  59. contentController.add(self, name: "toggleVoiceSearch")
  60. contentController.add(self, name: "blockUser")
  61. contentController.add(self, name: "showAlert")
  62. contentController.add(self, name: "closeProfile")
  63. contentController.add(self, name: "tabShowHide")
  64. contentController.add(self, name: "shareText")
  65. contentController.add(self, name: "openGalleryiOS")
  66. let source: String = "var meta = document.createElement('meta');" +
  67. "meta.name = 'viewport';" +
  68. "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
  69. "var head = document.getElementsByTagName('head')[0];" +
  70. "head.appendChild(meta);" +
  71. "$('#header-layout').find('.col-8').removeClass('col-8').addClass('col');" +
  72. "$('#header-layout').find('.col-4').removeClass('col-4').addClass('col');"
  73. let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
  74. contentController.addUserScript(script)
  75. let cookieScript1 = "document.cookie = '\(Utils.getCookiesMobile().components(separatedBy: ";")[0])';"
  76. let cookieScriptInjection1 = WKUserScript(source: cookieScript1, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  77. configuration.userContentController.addUserScript(cookieScriptInjection1)
  78. let cookieScript2 = "document.cookie = '\(Utils.getCookiesMobile().components(separatedBy: ";")[1])';"
  79. let cookieScriptInjection2 = WKUserScript(source: cookieScript2, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  80. configuration.userContentController.addUserScript(cookieScriptInjection2)
  81. NotificationCenter.default.addObserver(self, selector: #selector(onShowAC(notification:)), name: NSNotification.Name(rawValue: "onShowAC"), object: nil)
  82. NotificationCenter.default.addObserver(self, selector: #selector(onRefreshWebView(notification:)), name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil)
  83. }
  84. func loadURLWithCookie(url: URL) {
  85. var urlRequest = URLRequest(url: url)
  86. let customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1 \(Utils.getUserAgent())"
  87. urlRequest.setValue(customUserAgent, forHTTPHeaderField: "User-Agent")
  88. if let cookies = HTTPCookieStorage.shared.cookies(for: url) {
  89. for cookie in cookies {
  90. webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
  91. }
  92. webView.load(urlRequest)
  93. }
  94. }
  95. override func viewWillAppear(_ animated: Bool) {
  96. let me = User.getMyPin()
  97. var myURL : URL?
  98. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  99. var intLang = 0
  100. if lang == "id" {
  101. intLang = 1
  102. }
  103. if PrefsUtil.getURLFirstTab() != nil {
  104. ViewController.sURL = PrefsUtil.getURLFirstTab()!
  105. }
  106. switch(ViewController.sURL){
  107. case "0":
  108. address = "\(PrefsUtil.getURLBase())nexilis/pages/tab1-main-only?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  109. myURL = URL(string: address)
  110. case "1":
  111. address = "\(PrefsUtil.getURLBase())nexilis/pages/tab3-main-only?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  112. myURL = URL(string: address)
  113. case "2":
  114. address = "\(PrefsUtil.getURLBase())nexilis/pages/tab1-main?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  115. myURL = URL(string: address)
  116. case "3":
  117. address = "\(PrefsUtil.getURLBase())nexilis/pages/tab3-commerce?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  118. myURL = URL(string: address)
  119. case "4":
  120. address = "\(PrefsUtil.getURLBase())nexilis/pages/tab1-video?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  121. myURL = URL(string: address)
  122. default:
  123. if(!ViewController.sURL.isEmpty){
  124. if(ViewController.sURL.lowercased().contains("https://") || ViewController.sURL.lowercased().contains("http://")){
  125. address = ViewController.sURL
  126. myURL = URL(string: address)
  127. }
  128. else {
  129. if ViewController.sURL.contains("nexilis/pages"){
  130. address = "\(PrefsUtil.getURLBase())\(ViewController.sURL)?f_pin=\(me ?? "")&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  131. } else {
  132. address = "https://\(ViewController.sURL)"
  133. }
  134. myURL = URL(string: address)
  135. }
  136. }
  137. }
  138. if let u = myURL {
  139. self.webView.evaluateJavaScript("{window.localStorage.setItem('currentTab','\(ViewController.sURL)')}")
  140. if FirstTabViewController.forceRefresh {
  141. loadURLWithCookie(url: u)
  142. } else {
  143. self.webView.evaluateJavaScript("if(resumeAll){resumeAll();}")
  144. }
  145. FirstTabViewController.forceRefresh = false
  146. }
  147. navigationController?.setNavigationBarHidden(true, animated: false)
  148. }
  149. func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  150. if Utils.getIsLoadThemeFromOther() {
  151. self.webView.evaluateJavaScript("{window.localStorage.setItem('mobileConfiguration','"+Utils.getMyTheme()+"')}")
  152. }
  153. }
  154. override func viewDidAppear(_ animated: Bool) {
  155. DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
  156. var viewController = UIApplication.shared.windows.first!.rootViewController
  157. if !(viewController is ViewController) {
  158. viewController = self.parent
  159. }
  160. if ViewController.middleButton.isHidden {
  161. ViewController.isExpandButton = false
  162. if let viewController = viewController as? ViewController {
  163. if viewController.tabBar.isHidden {
  164. viewController.tabBar.isHidden = false
  165. ViewController.middleButton.isHidden = false
  166. ViewController.alwaysHideButton = false
  167. }
  168. }
  169. } else if PrefsUtil.getCpaasMode() != PrefsUtil.CPAAS_MODE_DOCKED {
  170. DispatchQueue.main.async {
  171. if let viewController = viewController as? ViewController {
  172. if viewController.tabBar.isHidden {
  173. viewController.tabBar.isHidden = false
  174. ViewController.alwaysHideButton = false
  175. }
  176. }
  177. }
  178. }
  179. })
  180. }
  181. @objc func onShowAC(notification: NSNotification) {
  182. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  183. view.endEditing(true)
  184. resignFirstResponder()
  185. }
  186. @objc func onRefreshWebView(notification: NSNotification) {
  187. FirstTabViewController.forceRefresh = true
  188. }
  189. override func viewWillDisappear(_ animated: Bool) {
  190. if self.webView.scrollView.contentOffset.y < 0 { // Move tableView to top
  191. self.webView.scrollView.setContentOffset(CGPoint.zero, animated: true)
  192. }
  193. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  194. self.webView.evaluateJavaScript("hideAddToCart();")
  195. }
  196. func scrollViewDidScroll(_ scrollView: UIScrollView) {
  197. if (self.lastContentOffset > scrollView.contentOffset.y && scrollView.contentOffset.y < (scrollView.contentSize.height - scrollView.frame.size.height)) {
  198. showTabBar();
  199. }
  200. else if (self.lastContentOffset != 0 && self.lastContentOffset < scrollView.contentOffset.y && self.lastContentOffset >= 0) {
  201. hideTabBar();
  202. }
  203. self.lastContentOffset = scrollView.contentOffset.y
  204. self.collapseDocked()
  205. }
  206. @objc func collapseDocked() {
  207. if ViewController.isExpandButton {
  208. ViewController.expandButton()
  209. }
  210. }
  211. @objc func reloadWebView(_ sender: UIRefreshControl) {
  212. webView.reload()
  213. ViewController.alwaysHideButton = false
  214. showTabBar()
  215. sender.endRefreshing()
  216. }
  217. func hideTabBar() {
  218. var viewController = UIApplication.shared.windows.first!.rootViewController
  219. if !(viewController is ViewController) {
  220. viewController = self.parent
  221. }
  222. if ViewController.middleButton.isDescendant(of: viewController!.view) {
  223. DispatchQueue.main.async {
  224. if ViewController.isExpandButton {
  225. ViewController.expandButton()
  226. }
  227. ViewController.hideDockedButton()
  228. if let viewController = viewController as? ViewController {
  229. viewController.tabBar.isHidden = true
  230. }
  231. ViewController.removeMiddleButton()
  232. }
  233. } else if PrefsUtil.getCpaasMode() != PrefsUtil.CPAAS_MODE_DOCKED {
  234. DispatchQueue.main.async {
  235. if let viewController = viewController as? ViewController {
  236. if !viewController.tabBar.isHidden {
  237. viewController.tabBar.isHidden = true
  238. }
  239. }
  240. }
  241. }
  242. }
  243. func showTabBar() {
  244. if(ViewController.alwaysHideButton){
  245. return
  246. }
  247. var viewController = UIApplication.shared.windows.first!.rootViewController
  248. if !(viewController is ViewController) {
  249. viewController = self.parent
  250. }
  251. if ViewController.middleButton.isHidden {
  252. if let viewController = viewController as? ViewController {
  253. viewController.tabBar.isHidden = false
  254. ViewController.middleButton.isHidden = false
  255. }
  256. } else if PrefsUtil.getCpaasMode() != PrefsUtil.CPAAS_MODE_DOCKED {
  257. DispatchQueue.main.async {
  258. if let viewController = viewController as? ViewController {
  259. if viewController.tabBar.isHidden {
  260. viewController.tabBar.isHidden = false
  261. }
  262. }
  263. }
  264. }
  265. }
  266. func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
  267. scrollView.pinchGestureRecognizer?.isEnabled = false
  268. }
  269. func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
  270. if message.name == "checkProfile" {
  271. guard let dict = message.body as? [String: AnyObject],
  272. let param1 = dict["param1"] as? String,
  273. let param2 = dict["param2"] as? String else {
  274. return
  275. }
  276. if ViewController.checkIsChangePerson() {
  277. if param2 == "like" {
  278. self.webView.evaluateJavaScript("likeProduct('\(param1)',1,true);")
  279. } else if param2 == "comment" {
  280. self.webView.evaluateJavaScript("openComment('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  281. } else if param2 == "report_user" {
  282. self.webView.evaluateJavaScript("reportUser('\(param1)',true);")
  283. } else if param2 == "report_content" {
  284. self.webView.evaluateJavaScript("reportContent('\(param1.split(separator: "|")[0])','\(param1.split(separator: "|")[1])',true);")
  285. } else if param2 == "block_user" {
  286. self.webView.evaluateJavaScript("blockUser('\(param1)',true);")
  287. } else if param2 == "follow_user" {
  288. self.webView.evaluateJavaScript("followUser('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  289. } else if param2 == "homepage" || param2 == "gif" {
  290. self.webView.evaluateJavaScript("window.location.href = '\(param1)';")
  291. } else if param2 == "block_content" {
  292. self.webView.evaluateJavaScript("blockContent('\(param1)',true);")
  293. } else {
  294. self.webView.evaluateJavaScript("openNewPost(true);")
  295. }
  296. } else {
  297. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  298. }
  299. } else if message.name == "setIsProductModalOpen" {
  300. guard let dict = message.body as? [String: AnyObject],
  301. let param1 = dict["param1"] as? Bool else {
  302. return
  303. }
  304. if param1 {
  305. if self.webView.scrollView.contentOffset.y < 0 { // Move tableView to top
  306. self.webView.scrollView.setContentOffset(CGPoint.zero, animated: true)
  307. }
  308. }
  309. FirstTabViewController.showModal = param1
  310. } else if message.name == "toggleVoiceSearch" {
  311. if !isAllowSpeech {
  312. setupSpeech()
  313. } else {
  314. runVoice()
  315. }
  316. } else if message.name == "blockUser" {
  317. guard let dict = message.body as? [String: AnyObject],
  318. let param1 = dict["param1"] as? String,
  319. let param2 = dict["param2"] as? Bool else {
  320. return
  321. }
  322. if param2 {
  323. DispatchQueue.global().async {
  324. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  325. if response.isOk() {
  326. DispatchQueue.main.async {
  327. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  328. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  329. "ex_block" : "1"
  330. ], _where: "f_pin = '\(param1)'")
  331. })
  332. }
  333. }
  334. }
  335. }
  336. } else {
  337. DispatchQueue.global().async {
  338. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  339. if response.isOk() {
  340. DispatchQueue.main.async {
  341. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  342. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  343. "ex_block" : "0"
  344. ], _where: "f_pin = '\(param1)'")
  345. })
  346. }
  347. }
  348. }
  349. }
  350. }
  351. } else if message.name == "showAlert" {
  352. guard let dict = message.body as? [String: AnyObject],
  353. let param1 = dict["param1"] as? String else {
  354. return
  355. }
  356. self.view.makeToast(param1, duration: 3)
  357. } else if message.name == "blockUser" {
  358. guard let dict = message.body as? [String: AnyObject],
  359. let param1 = dict["param1"] as? String,
  360. let param2 = dict["param2"] as? Bool else {
  361. return
  362. }
  363. if param2 {
  364. DispatchQueue.global().async {
  365. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  366. if response.isOk() {
  367. DispatchQueue.main.async {
  368. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  369. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  370. "ex_block" : "1"
  371. ], _where: "f_pin = '\(param1)'")
  372. })
  373. }
  374. }
  375. }
  376. }
  377. } else {
  378. DispatchQueue.global().async {
  379. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  380. if response.isOk() {
  381. DispatchQueue.main.async {
  382. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  383. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  384. "ex_block" : "0"
  385. ], _where: "f_pin = '\(param1)'")
  386. })
  387. }
  388. }
  389. }
  390. }
  391. }
  392. } else if message.name == "tabShowHide" {
  393. guard let dict = message.body as? [String: AnyObject],
  394. let param1 = dict["param1"] as? Bool else {
  395. return
  396. }
  397. if param1 {
  398. ViewController.alwaysHideButton = false
  399. showTabBar()
  400. } else {
  401. if self.viewIfLoaded?.window != nil {
  402. ViewController.alwaysHideButton = true
  403. hideTabBar()
  404. }
  405. }
  406. } else if message.name == "shareText" {
  407. guard let dict = message.body as? [String: AnyObject],
  408. let param1 = dict["param1"] as? String else {
  409. return
  410. }
  411. let activityViewController = UIActivityViewController(activityItems: [param1], applicationActivities: nil)
  412. self.present(activityViewController, animated: true, completion: nil)
  413. } else if message.name == "openGalleryiOS" {
  414. guard let dict = message.body as? [String: AnyObject],
  415. let param1 = dict["param1"] as? Int else {
  416. return
  417. }
  418. indexImageVideoWv = param1
  419. let alertController = LibAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
  420. if let action = self.actionImageVideo(for: "image", title: "Choose Photo".localized()) {
  421. alertController.addAction(action)
  422. }
  423. if let action = self.actionImageVideo(for: "video", title: "Choose Video".localized()) {
  424. alertController.addAction(action)
  425. }
  426. alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
  427. self.present(alertController, animated: true)
  428. }
  429. }
  430. private func actionImageVideo(for type: String, title: String) -> UIAlertAction? {
  431. return UIAlertAction(title: title, style: .default) { [unowned self] _ in
  432. switch type {
  433. case "image":
  434. imageVideoPicker.present(source: .imageAlbum)
  435. case "video":
  436. imageVideoPicker.present(source: .videoAlbum)
  437. default:
  438. imageVideoPicker.present(source: .imageAlbum)
  439. }
  440. }
  441. }
  442. func didSelect(imagevideo: Any?) {
  443. if imagevideo != nil {
  444. let imageData = imagevideo! as! [UIImagePickerController.InfoKey : Any]
  445. if (imageData[.mediaType] as! String == "public.image") {
  446. let compressedImage = (imageData[.originalImage] as! UIImage).pngData()!
  447. let base64String = compressedImage.base64EncodedString()
  448. let base64ToWeb = "data:image/jpeg;base64,\(base64String)"
  449. webView.evaluateJavaScript("loadFromMobile('\(base64ToWeb)',\(indexImageVideoWv))") { (result, error) in
  450. if let error = error {
  451. print("Error executing JavaScript: \(error)")
  452. }
  453. }
  454. } else {
  455. guard var dataVideo = try? Data(contentsOf: imageData[.mediaURL] as! URL) else {
  456. return
  457. }
  458. let sizeOfVideo = Double(dataVideo.count / 1048576)
  459. if (sizeOfVideo > 10.0) {
  460. let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + UUID().uuidString + ".mp4")
  461. compressVideo(inputURL: imageData[.mediaURL] as! URL,
  462. outputURL: compressedURL) { exportSession in
  463. guard let session = exportSession else {
  464. return
  465. }
  466. switch session.status {
  467. case .unknown:
  468. break
  469. case .waiting:
  470. break
  471. case .exporting:
  472. break
  473. case .completed:
  474. guard let compressedData = try? Data(contentsOf: compressedURL) else {
  475. return
  476. }
  477. dataVideo = compressedData
  478. case .failed:
  479. break
  480. case .cancelled:
  481. break
  482. @unknown default:
  483. break
  484. }
  485. }
  486. }
  487. let base64String = dataVideo.base64EncodedString()
  488. let base64ToWeb = "data:video/mp4;base64,\(base64String)"
  489. webView.evaluateJavaScript("loadFromMobile('\(base64ToWeb)',\(indexImageVideoWv))") { (result, error) in
  490. if let error = error {
  491. print("Error executing JavaScript: \(error)")
  492. }
  493. }
  494. }
  495. }
  496. }
  497. func compressVideo(inputURL: URL,
  498. outputURL: URL,
  499. handler:@escaping (_ exportSession: AVAssetExportSession?) -> Void) {
  500. let urlAsset = AVURLAsset(url: inputURL, options: nil)
  501. guard let exportSession = AVAssetExportSession(asset: urlAsset,
  502. presetName: AVAssetExportPresetMediumQuality) else {
  503. handler(nil)
  504. return
  505. }
  506. exportSession.outputURL = outputURL
  507. exportSession.outputFileType = .mp4
  508. exportSession.exportAsynchronously {
  509. handler(exportSession)
  510. }
  511. }
  512. func setupSpeech() {
  513. self.speechRecognizer?.delegate = self
  514. SFSpeechRecognizer.requestAuthorization { (authStatus) in
  515. var isButtonEnabled = false
  516. switch authStatus {
  517. case .authorized:
  518. isButtonEnabled = true
  519. case .denied:
  520. isButtonEnabled = false
  521. //print("User denied access to speech recognition")
  522. case .restricted:
  523. isButtonEnabled = false
  524. //print("Speech recognition restricted on this device")
  525. case .notDetermined:
  526. isButtonEnabled = false
  527. //print("Speech recognition not yet authorized")
  528. @unknown default:
  529. isButtonEnabled = false
  530. }
  531. OperationQueue.main.addOperation() {
  532. self.isAllowSpeech = isButtonEnabled
  533. if isButtonEnabled {
  534. SecureUserDefaults.shared.set(isButtonEnabled, forKey: "allowSpeech")
  535. self.runVoice()
  536. }
  537. }
  538. }
  539. }
  540. func runVoice() {
  541. if !audioEngine.isRunning {
  542. alertController = LibAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
  543. self.present(alertController, animated: true)
  544. self.webView.evaluateJavaScript("toggleVoiceButton(true)")
  545. self.startRecording()
  546. }
  547. }
  548. func startRecording() {
  549. // Clear all previous session data and cancel task
  550. if recognitionTask != nil {
  551. recognitionTask?.cancel()
  552. recognitionTask = nil
  553. }
  554. // Create instance of audio session to record voice
  555. let audioSession = AVAudioSession.sharedInstance()
  556. do {
  557. try audioSession.setCategory(AVAudioSession.Category.record, mode: .default, options: [])
  558. try audioSession.setMode(AVAudioSession.Mode.measurement)
  559. try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
  560. } catch {
  561. //print("audioSession properties weren't set because of an error.")
  562. }
  563. self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
  564. let inputNode = audioEngine.inputNode
  565. guard let recognitionRequest = recognitionRequest else {
  566. fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
  567. }
  568. recognitionRequest.shouldReportPartialResults = true
  569. self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
  570. var isFinal = false
  571. var text = ""
  572. if result != nil {
  573. text = result?.bestTranscription.formattedString ?? ""
  574. isFinal = (result?.isFinal)!
  575. self.alertController.dismiss(animated: true)
  576. self.audioEngine.stop()
  577. self.recognitionRequest?.endAudio()
  578. } else {
  579. self.alertController.dismiss(animated: true)
  580. }
  581. if error != nil || isFinal {
  582. if error == nil {
  583. self.webView.evaluateJavaScript("toggleVoiceButton(false)")
  584. self.webView.evaluateJavaScript("submitVoiceSearch('\(text)')")
  585. } else {
  586. self.audioEngine.stop()
  587. self.recognitionRequest?.endAudio()
  588. }
  589. inputNode.removeTap(onBus: 0)
  590. self.recognitionRequest = nil
  591. self.recognitionTask = nil
  592. self.isAllowSpeech = true
  593. }
  594. })
  595. let recordingFormat = inputNode.outputFormat(forBus: 0)
  596. inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
  597. self.recognitionRequest?.append(buffer)
  598. }
  599. self.audioEngine.prepare()
  600. do {
  601. try self.audioEngine.start()
  602. } catch {
  603. //print("audioEngine couldn't start because of an error.")
  604. }
  605. }
  606. func isUsingMyWebview() -> Bool{
  607. return PrefsUtil.getURLFirstTab() == "0" || PrefsUtil.getURLFirstTab() == "1" || PrefsUtil.getURLFirstTab() == "2" || PrefsUtil.getURLFirstTab() == "3" || PrefsUtil.getURLFirstTab() == "4"
  608. }
  609. }
  610. extension FirstTabViewController: SFSpeechRecognizerDelegate {
  611. func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
  612. if available {
  613. self.isAllowSpeech = true
  614. } else {
  615. self.isAllowSpeech = false
  616. }
  617. }
  618. }
  619. extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
  620. func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) {
  621. guard let url = navigationAction.request.url else {
  622. decisionHandler(.cancel)
  623. return
  624. }
  625. if allowedURLs.contains(url.absoluteString) {
  626. if ViewController.alwaysHideButton {
  627. ViewController.alwaysHideButton = false
  628. showTabBar()
  629. }
  630. print("✅ URL already allowed: \(url)")
  631. decisionHandler(.allow)
  632. return
  633. }
  634. validateSSLCertificate(url: url) { isValid in
  635. print("is VALID? : \(isValid)")
  636. if isValid {
  637. self.allowedURLs.insert(url.absoluteString)
  638. decisionHandler(.allow)
  639. } else {
  640. let host = url.host ?? ""
  641. DispatchQueue.main.async {
  642. var messageText = "You're about to access a website that is not currently trusted by your Nexilis Browser. This website's security certificate is not recognized.\n\nDo you wish to proceed to <<domain>> and trust the website's security certificate?\n\nNote: Adding a website to the trusted list may increase your risk of security vulnerability".localized()
  643. messageText = messageText.replacingOccurrences(of: "<<domain>>", with: host)
  644. let alert = UIAlertController(title: "Warning Unknown Url!".localized(),
  645. message: messageText,
  646. preferredStyle: .alert)
  647. let yesAction = UIAlertAction(title: "Yes", style: .default) { _ in
  648. let storedCertificate = Utils.getCertificatePinningWebview()
  649. if let jsonData = storedCertificate.data(using: .utf8),
  650. let certJson = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: String] {
  651. var certJson = certJson
  652. certJson[host] = self.blockedCertificate
  653. if let jsonData = try? JSONSerialization.data(withJSONObject: certJson, options: []),
  654. let jsonString = String(data: jsonData, encoding: .utf8) {
  655. Utils.setCertificatePinningWebview(value: jsonString)
  656. }
  657. }
  658. self.allowedURLs.insert(url.absoluteString)
  659. decisionHandler(.allow)
  660. }
  661. let noAction = UIAlertAction(title: "No", style: .cancel) { _ in
  662. decisionHandler(.cancel)
  663. }
  664. alert.addAction(yesAction)
  665. alert.addAction(noAction)
  666. self.present(alert, animated: true, completion: nil)
  667. }
  668. }
  669. }
  670. }
  671. private func validateSSLCertificate(url: URL, completion: @escaping (Bool) -> Void) {
  672. let session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil)
  673. let request = URLRequest(url: url)
  674. let task = session.dataTask(with: request) { _, response, error in
  675. if let error = error {
  676. print("SSL Validation Error: \(error.localizedDescription)")
  677. completion(false)
  678. return
  679. }
  680. completion(true)
  681. }
  682. task.resume()
  683. }
  684. }
  685. extension FirstTabViewController: URLSessionDelegate {
  686. func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
  687. guard let serverTrust = challenge.protectionSpace.serverTrust else {
  688. completionHandler(.cancelAuthenticationChallenge, nil)
  689. return
  690. }
  691. if let publicKeyHash = extractPublicKeyHash(from: serverTrust) {
  692. let domain = challenge.protectionSpace.host
  693. let storedCertificate = Utils.getCertificatePinningWebview()
  694. if let jsonData = storedCertificate.data(using: .utf8),
  695. let certJson = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: String] {
  696. if publicKeyHash == certJson[domain] {
  697. print("✅ Certificate Matched. Allowing Navigation.")
  698. completionHandler(.useCredential, URLCredential(trust: serverTrust))
  699. } else {
  700. print("❌ Certificate Mismatch! Blocking Navigation.")
  701. blockedCertificate = publicKeyHash
  702. completionHandler(.cancelAuthenticationChallenge, nil)
  703. }
  704. }
  705. } else {
  706. completionHandler(.cancelAuthenticationChallenge, nil)
  707. }
  708. }
  709. func extractPublicKeyHash(from serverTrust: SecTrust) -> String? {
  710. guard let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else { return nil }
  711. guard let publicKey = SecCertificateCopyKey(certificate) else { return nil }
  712. var error: Unmanaged<CFError>?
  713. guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
  714. print("❌ Failed to extract public key")
  715. return nil
  716. }
  717. // Compute SHA-256 hash
  718. var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
  719. publicKeyData.withUnsafeBytes {
  720. _ = CC_SHA256($0.baseAddress, CC_LONG(publicKeyData.count), &hash)
  721. }
  722. let hashData = Data(hash)
  723. let base64Hash = hashData.base64EncodedString()
  724. return base64Hash
  725. }
  726. }