BNIBookingWebView.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. //
  2. // BNIBookingWebView.swift
  3. // FMDB
  4. //
  5. // Created by Qindi on 01/04/22.
  6. //
  7. import Foundation
  8. import UIKit
  9. import WebKit
  10. import Speech
  11. public class BNIBookingWebView: UIViewController, WKNavigationDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKScriptMessageHandler, SFSpeechRecognizerDelegate {
  12. var webView = WKWebView()
  13. let closeButton = UIButton()
  14. public var customUrl = ""
  15. var isAllowSpeech = false
  16. let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
  17. var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
  18. var recognitionTask : SFSpeechRecognitionTask?
  19. let audioEngine = AVAudioEngine()
  20. var alertController = LibAlertController()
  21. public override var preferredStatusBarStyle: UIStatusBarStyle {
  22. return .default
  23. }
  24. public override func viewDidLoad() {
  25. super.viewDidLoad()
  26. let configuration = WKWebViewConfiguration()
  27. configuration.allowsInlineMediaPlayback = true
  28. 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())"
  29. let finalUserAgent = "\(customUserAgent)"
  30. configuration.applicationNameForUserAgent = finalUserAgent
  31. webView = WKWebView(frame: .zero, configuration: configuration)
  32. view.addSubview(webView)
  33. webView.translatesAutoresizingMaskIntoConstraints = false
  34. webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
  35. webView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
  36. webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
  37. webView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
  38. webView.navigationDelegate = self
  39. webView.allowsBackForwardNavigationGestures = true
  40. webView.scrollView.delegate = self
  41. let contentController = webView.configuration.userContentController
  42. contentController.add(self, name: "sendQueueBNI")
  43. contentController.add(self, name: "checkProfile")
  44. contentController.add(self, name: "setIsProductModalOpen")
  45. contentController.add(self, name: "toggleVoiceSearch")
  46. contentController.add(self, name: "blockUser")
  47. contentController.add(self, name: "showAlert")
  48. contentController.add(self, name: "closeProfile")
  49. contentController.add(self, name: "successChangeTheme")
  50. contentController.add(self, name: "finishForm")
  51. let source: String = "var meta = document.createElement('meta');" +
  52. "meta.name = 'viewport';" +
  53. "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
  54. "var head = document.getElementsByTagName('head')[0];" +
  55. "head.appendChild(meta);" +
  56. "$('#header-layout').find('.col-8').removeClass('col-8').addClass('col');" +
  57. "$('#header-layout').find('.col-4').removeClass('col-4').addClass('col');"
  58. let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
  59. contentController.addUserScript(script)
  60. let cookieScript1 = "document.cookie = '\(Utils.getCookiesMobile().components(separatedBy: ";")[0])';"
  61. let cookieScriptInjection1 = WKUserScript(source: cookieScript1, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  62. configuration.userContentController.addUserScript(cookieScriptInjection1)
  63. let cookieScript2 = "document.cookie = '\(Utils.getCookiesMobile().components(separatedBy: ";")[1])';"
  64. let cookieScriptInjection2 = WKUserScript(source: cookieScript2, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  65. configuration.userContentController.addUserScript(cookieScriptInjection2)
  66. let refreshControl = UIRefreshControl()
  67. refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
  68. webView.scrollView.addSubview(refreshControl)
  69. webView.isOpaque = false
  70. webView.backgroundColor = .white
  71. webView.scrollView.backgroundColor = .white
  72. var stringQMS = "https://sqbni.murni.id:4200/bnibookingonline/#/?userid="
  73. if !customUrl.isEmpty {
  74. stringQMS = customUrl
  75. }
  76. if stringQMS.lowercased().contains("?userid=") {
  77. let name = User.getData(pin: User.getMyPin())!.fullName
  78. stringQMS += name
  79. } else if stringQMS.lowercased().contains("?f_pin=") {
  80. stringQMS += User.getMyPin()!
  81. }
  82. if stringQMS.contains("<<f_pin>>") {
  83. stringQMS = stringQMS.replacingOccurrences(of: "<<f_pin>>", with: User.getMyPin()!)
  84. }
  85. let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
  86. var intLang = 0
  87. if lang == "id" {
  88. intLang = 1
  89. }
  90. stringQMS = stringQMS + "&lang=\(intLang)&theme=\(self.traitCollection.userInterfaceStyle == .dark ? "0" : "1")"
  91. let url = URL(string: "\(stringQMS)")!
  92. loadURLWithCookie(url: url)
  93. }
  94. func loadURLWithCookie(url: URL) {
  95. var urlRequest = URLRequest(url: url)
  96. let cookieHeader = Utils.getCookiesMobile()
  97. urlRequest.addValue(cookieHeader, forHTTPHeaderField: "Cookie")
  98. 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())"
  99. urlRequest.setValue(customUserAgent, forHTTPHeaderField: "User-Agent")
  100. if let cookies = HTTPCookieStorage.shared.cookies {
  101. for cookie in cookies {
  102. webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
  103. }
  104. }
  105. webView.load(urlRequest)
  106. }
  107. public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
  108. return true
  109. }
  110. public func scrollViewDidScroll(_ scrollView: UIScrollView) {
  111. }
  112. public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
  113. if message.name == "sendQueueBNI" {
  114. guard let dict = message.body as? [String: AnyObject],
  115. let param1 = dict["param1"] as? String else {
  116. return
  117. }
  118. DispatchQueue.global().async {
  119. let _ = Nexilis.writeSync(message: CoreMessage_TMessageBank.queueBNI(service_id: param1), timeout: 30 * 1000)
  120. }
  121. }
  122. if message.name == "checkProfile" {
  123. guard let dict = message.body as? [String: AnyObject],
  124. let param1 = dict["param1"] as? String,
  125. let param2 = dict["param2"] as? String else {
  126. return
  127. }
  128. if Nexilis.checkIsChangePerson() {
  129. if param2 == "like" {
  130. self.webView.evaluateJavaScript("likeProduct('\(param1)',1,true);")
  131. } else if param2 == "comment" {
  132. self.webView.evaluateJavaScript("openComment('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  133. } else if param2 == "report_user" {
  134. self.webView.evaluateJavaScript("reportUser('\(param1)',true);")
  135. } else if param2 == "report_content" {
  136. self.webView.evaluateJavaScript("reportContent('\(param1.split(separator: "|")[0])','\(param1.split(separator: "|")[1])',true);")
  137. } else if param2 == "block_user" {
  138. self.webView.evaluateJavaScript("blockUser('\(param1)',true);")
  139. } else if param2 == "follow_user" {
  140. self.webView.evaluateJavaScript("followUser('\(param1.split(separator: "|")[0])',\(param1.split(separator: "|")[1]),true);")
  141. } else if param2 == "homepage" || param2 == "gif" {
  142. self.webView.evaluateJavaScript("window.location.href = '\(param1)';")
  143. } else if param2 == "block_content" {
  144. self.webView.evaluateJavaScript("blockContent('\(param1)',true);")
  145. } else {
  146. self.webView.evaluateJavaScript("openNewPost(true);")
  147. }
  148. } else {
  149. self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
  150. }
  151. } else if message.name == "setIsProductModalOpen" {
  152. guard let dict = message.body as? [String: AnyObject],
  153. let param1 = dict["param1"] as? Bool else {
  154. return
  155. }
  156. if param1 {
  157. if self.webView.scrollView.contentOffset.y < 0 { // Move tableView to top
  158. self.webView.scrollView.setContentOffset(CGPoint.zero, animated: true)
  159. }
  160. }
  161. } else if message.name == "toggleVoiceSearch" {
  162. if !isAllowSpeech {
  163. setupSpeech()
  164. } else {
  165. runVoice()
  166. }
  167. } else if message.name == "blockUser" {
  168. guard let dict = message.body as? [String: AnyObject],
  169. let param1 = dict["param1"] as? String,
  170. let param2 = dict["param2"] as? Bool else {
  171. return
  172. }
  173. if param2 {
  174. DispatchQueue.global().async {
  175. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  176. if response.isOk() {
  177. DispatchQueue.main.async {
  178. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  179. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  180. "ex_block" : "1"
  181. ], _where: "f_pin = '\(param1)'")
  182. })
  183. }
  184. }
  185. }
  186. }
  187. } else {
  188. DispatchQueue.global().async {
  189. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  190. if response.isOk() {
  191. DispatchQueue.main.async {
  192. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  193. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  194. "ex_block" : "0"
  195. ], _where: "f_pin = '\(param1)'")
  196. })
  197. }
  198. }
  199. }
  200. }
  201. }
  202. } else if message.name == "showAlert" {
  203. guard let dict = message.body as? [String: AnyObject],
  204. let param1 = dict["param1"] as? String else {
  205. return
  206. }
  207. showToast(message: param1, controller: self.tabBarController!)
  208. } else if message.name == "blockUser" {
  209. guard let dict = message.body as? [String: AnyObject],
  210. let param1 = dict["param1"] as? String,
  211. let param2 = dict["param2"] as? Bool else {
  212. return
  213. }
  214. if param2 {
  215. DispatchQueue.global().async {
  216. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
  217. if response.isOk() {
  218. DispatchQueue.main.async {
  219. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  220. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  221. "ex_block" : "1"
  222. ], _where: "f_pin = '\(param1)'")
  223. })
  224. }
  225. }
  226. }
  227. }
  228. } else {
  229. DispatchQueue.global().async {
  230. if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
  231. if response.isOk() {
  232. DispatchQueue.main.async {
  233. Database.shared.database?.inTransaction({ (fmdb, rollback) in
  234. _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
  235. "ex_block" : "0"
  236. ], _where: "f_pin = '\(param1)'")
  237. })
  238. }
  239. }
  240. }
  241. }
  242. }
  243. } else if message.name == "successChangeTheme" {
  244. guard let dict = message.body as? [String: AnyObject],
  245. let param1 = dict["param1"] as? String,
  246. let param2 = dict["param2"] as? Bool else {
  247. return
  248. }
  249. Utils.setMyTheme(value: param1)
  250. Utils.setIsLoadThemeFromOther(value: true)
  251. Utils.resetValueSuperApp()
  252. if let jsonArray = try! JSONSerialization.jsonObject(with: param1.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
  253. do {
  254. for json in jsonArray {
  255. if json["KEY"] as! String == "app_builder_url_webview_1" {
  256. Utils.setURLFirstTab(value: json["VALUE"] as! String)
  257. }
  258. if json["KEY"] as! String == "app_builder_url_webview_2" {
  259. Utils.setURLThirdTab(value: json["VALUE"] as! String)
  260. }
  261. if json["KEY"] as! String == "app_builder_url_webview_3" {
  262. Utils.setURLWv3(value: json["VALUE"] as! String)
  263. }
  264. if json["KEY"] as! String == "app_builder_url_webview_4" {
  265. Utils.setURLWv4(value: json["VALUE"] as! String)
  266. }
  267. if json["KEY"] as! String == "app_builder_url_webview_5" {
  268. Utils.setURLWv5(value: json["VALUE"] as! String)
  269. }
  270. if json["KEY"] as! String == "app_builder_url_webview_6" {
  271. Utils.setURLWv6(value: json["VALUE"] as! String)
  272. }
  273. if json["KEY"] as! String == "app_builder_custom_tab" {
  274. Utils.setCustomTab(cust: json["VALUE"] as! String)
  275. }
  276. if json["KEY"] as! String == "app_builder_icon_dock" {
  277. Utils.setIconDock(value: json["VALUE"] as! String)
  278. }
  279. if json["KEY"] as! String == "app_builder_background" {
  280. Utils.setBackground(value: json["VALUE"] as! String)
  281. }
  282. if json["KEY"] as! String == "app_builder_background_light" {
  283. Utils.setBackgroundLight(value: json["VALUE"] as! String)
  284. }
  285. if json["KEY"] as! String == "app_builder_background_dark" {
  286. Utils.setBackgroundDark(value: json["VALUE"] as! String)
  287. }
  288. if json["KEY"] as! String == "app_builder_background_1" {
  289. Utils.setBackgroundTab1(value: json["VALUE"] as! String)
  290. }
  291. if json["KEY"] as! String == "app_builder_background_2" {
  292. Utils.setBackgroundTab2(value: json["VALUE"] as! String)
  293. }
  294. if json["KEY"] as! String == "app_builder_background_3" {
  295. Utils.setBackgroundTab3(value: json["VALUE"] as! String)
  296. }
  297. if json["KEY"] as! String == "app_builder_background_4" {
  298. Utils.setBackgroundTab4(value: json["VALUE"] as! String)
  299. }
  300. if json["KEY"] as! String == "app_builder_background_5" {
  301. Utils.setBackgroundTab5(value: json["VALUE"] as! String)
  302. }
  303. if json["KEY"] as! String == "app_builder_background_6" {
  304. Utils.setBackgroundTab6(value: json["VALUE"] as! String)
  305. }
  306. if json["KEY"] as! String == "access_model" {
  307. Utils.setCpaasMode(mode: Int(json["VALUE"] as! String) ?? 0)
  308. }
  309. if json["KEY"] as! String == "app_builder_custom_buttons" {
  310. Utils.setCustomButtons(value: json["VALUE"] as! String)
  311. }
  312. if json["KEY"] as! String == "cpaas_icon" {
  313. Utils.setIconDock(value: json["VALUE"] as! String)
  314. }
  315. if json["KEY"] as! String == "tab1_icon" {
  316. Utils.setTab1Icon(value: json["VALUE"] as! String)
  317. }
  318. if json["KEY"] as! String == "tab2_icon" {
  319. Utils.setTab2Icon(value: json["VALUE"] as! String)
  320. }
  321. if json["KEY"] as! String == "tab3_icon" {
  322. Utils.setTab3Icon(value: json["VALUE"] as! String)
  323. }
  324. if json["KEY"] as! String == "tab4_icon" {
  325. Utils.setTab4Icon(value: json["VALUE"] as! String)
  326. }
  327. if json["KEY"] as! String == "tab5_icon" {
  328. Utils.setTab5Icon(value: json["VALUE"] as! String)
  329. }
  330. if json["KEY"] as! String == "tab6_icon" {
  331. Utils.setTab6Icon(value: json["VALUE"] as! String)
  332. }
  333. if json["KEY"] as! String == "app_builder_button_icon" {
  334. Utils.setButtonIcon(value: json["VALUE"] as! String)
  335. }
  336. if json["KEY"] as! String == "reverse_tab_color" {
  337. Utils.setReverseTab(value: json["VALUE"] as! String)
  338. }
  339. if json["KEY"] as! String == "icon_size" {
  340. Utils.setIconDockSize(value: json["VALUE"] as! String)
  341. }
  342. if json["KEY"] as! String == "app_id" {
  343. changeIconApp(appId: json["VALUE"] as! String)
  344. }
  345. }
  346. } catch {
  347. }
  348. }
  349. Database.shared.database?.inTransaction({ fmdb, rollback in
  350. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ", _where: "")
  351. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "GROUPZ_MEMBER", _where: "")
  352. _ = Database.shared.deleteRecord(fmdb: fmdb, table: "DISCUSSION_FORUM", _where: "")
  353. _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: User.getMyPin() ?? ""))
  354. })
  355. let alert = LibAlertController(title: "Successfully changed".localized(), message: "Please open the app again to see the changes".localized(), preferredStyle: .alert)
  356. alert.addAction(UIAlertAction(title: "OK".localized(), style: .default, handler: {(_) in
  357. exit(0)
  358. }))
  359. self.present(alert, animated: true, completion: nil)
  360. } else if message.name == "finishForm" {
  361. if self.webView.canGoBack {
  362. self.webView.goBack()
  363. } else {
  364. self.dismiss(animated: true)
  365. }
  366. }
  367. }
  368. func changeIconApp(appId: String) {
  369. let digisalesKey = "1694457466830"
  370. let nuKey = "1693550500075"
  371. let iknKey = "1693542580518"
  372. let diginetsKey = "1693456149709"
  373. let biKey = "1692873053159"
  374. let nxcookKey = "1692863737543"
  375. let nxsportKey = "1692863037019"
  376. let bpkhKey = "1711023277251"
  377. let disiniKey = "1711024221024"
  378. let gudegKey = "1712052403416"
  379. let kmiKey = "1713407687550"
  380. switch appId {
  381. case digisalesKey:
  382. UIApplication.shared.setAlternateIconName("digisales_icon")
  383. case nuKey:
  384. UIApplication.shared.setAlternateIconName("nu_icon")
  385. case iknKey:
  386. UIApplication.shared.setAlternateIconName("ikn_icon")
  387. case diginetsKey:
  388. UIApplication.shared.setAlternateIconName("diginets_icon")
  389. case biKey:
  390. UIApplication.shared.setAlternateIconName("bi_icon")
  391. case nxcookKey:
  392. UIApplication.shared.setAlternateIconName("nxcook_icon")
  393. case nxsportKey:
  394. UIApplication.shared.setAlternateIconName("nxsport_icon")
  395. case bpkhKey:
  396. UIApplication.shared.setAlternateIconName("bpkh_icon")
  397. case disiniKey:
  398. UIApplication.shared.setAlternateIconName("disini_icon")
  399. case gudegKey:
  400. UIApplication.shared.setAlternateIconName("gudeg_icon")
  401. case kmiKey:
  402. UIApplication.shared.setAlternateIconName("kmi_icon")
  403. default:
  404. UIApplication.shared.setAlternateIconName(nil)
  405. }
  406. }
  407. func setupSpeech() {
  408. self.speechRecognizer?.delegate = self
  409. SFSpeechRecognizer.requestAuthorization { (authStatus) in
  410. var isButtonEnabled = false
  411. switch authStatus {
  412. case .authorized:
  413. isButtonEnabled = true
  414. case .denied:
  415. isButtonEnabled = false
  416. //print("User denied access to speech recognition")
  417. case .restricted:
  418. isButtonEnabled = false
  419. //print("Speech recognition restricted on this device")
  420. case .notDetermined:
  421. isButtonEnabled = false
  422. //print("Speech recognition not yet authorized")
  423. @unknown default:
  424. isButtonEnabled = false
  425. }
  426. OperationQueue.main.addOperation() {
  427. self.isAllowSpeech = isButtonEnabled
  428. if isButtonEnabled {
  429. SecureUserDefaults.shared.set(isButtonEnabled, forKey: "allowSpeech")
  430. self.runVoice()
  431. }
  432. }
  433. }
  434. }
  435. func runVoice() {
  436. if !audioEngine.isRunning {
  437. alertController = LibAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
  438. self.present(alertController, animated: true)
  439. self.webView.evaluateJavaScript("toggleVoiceButton(true)")
  440. self.startRecording()
  441. }
  442. }
  443. func startRecording() {
  444. // Clear all previous session data and cancel task
  445. if recognitionTask != nil {
  446. recognitionTask?.cancel()
  447. recognitionTask = nil
  448. }
  449. // Create instance of audio session to record voice
  450. let audioSession = AVAudioSession.sharedInstance()
  451. do {
  452. try audioSession.setCategory(AVAudioSession.Category.record, mode: .default, options: [])
  453. try audioSession.setMode(AVAudioSession.Mode.measurement)
  454. try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
  455. } catch {
  456. //print("audioSession properties weren't set because of an error.")
  457. }
  458. self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
  459. let inputNode = audioEngine.inputNode
  460. guard let recognitionRequest = recognitionRequest else {
  461. fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
  462. }
  463. recognitionRequest.shouldReportPartialResults = true
  464. self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
  465. var isFinal = false
  466. var text = ""
  467. if result != nil {
  468. text = result?.bestTranscription.formattedString ?? ""
  469. isFinal = (result?.isFinal)!
  470. self.alertController.dismiss(animated: true)
  471. self.audioEngine.stop()
  472. self.recognitionRequest?.endAudio()
  473. } else {
  474. self.alertController.dismiss(animated: true)
  475. }
  476. if error != nil || isFinal {
  477. if error == nil {
  478. self.webView.evaluateJavaScript("toggleVoiceButton(false)")
  479. self.webView.evaluateJavaScript("submitVoiceSearch('\(text)')")
  480. } else {
  481. self.audioEngine.stop()
  482. self.recognitionRequest?.endAudio()
  483. }
  484. inputNode.removeTap(onBus: 0)
  485. self.recognitionRequest = nil
  486. self.recognitionTask = nil
  487. self.isAllowSpeech = true
  488. }
  489. })
  490. let recordingFormat = inputNode.outputFormat(forBus: 0)
  491. inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
  492. self.recognitionRequest?.append(buffer)
  493. }
  494. self.audioEngine.prepare()
  495. do {
  496. try self.audioEngine.start()
  497. } catch {
  498. //print("audioEngine couldn't start because of an error.")
  499. }
  500. }
  501. @objc func reloadWebView(_ sender: UIRefreshControl) {
  502. webView.reload()
  503. sender.endRefreshing()
  504. }
  505. public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
  506. if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
  507. let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
  508. completionHandler(.useCredential, credential)
  509. }
  510. else {
  511. completionHandler(.cancelAuthenticationChallenge, nil)
  512. }
  513. }
  514. }