FirstTabViewController.swift 38 KB

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