kevin 3 éve
szülő
commit
5209254122

+ 283 - 8
appbuilder-ios/AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -7,13 +7,24 @@
 
 import UIKit
 import WebKit
+import NexilisLite
+import Speech
 
-class FirstTabViewController: UIViewController, WKUIDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate {
+class FirstTabViewController: UIViewController, WKUIDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKScriptMessageHandler {
     
     @IBOutlet weak var webView: WKWebView!
     var address = ""
     private var lastContentOffset: CGFloat = 0
     
+    var isAllowSpeech = false
+    
+    let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
+
+    var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
+    var recognitionTask : SFSpeechRecognitionTask?
+    let audioEngine = AVAudioEngine()
+    var alertController = UIAlertController()
+    
     override func viewDidLoad() {
         super.viewDidLoad()
         let cpaasMode = PrefsUtil.getCpaasMode()
@@ -29,6 +40,24 @@ class FirstTabViewController: UIViewController, WKUIDelegate, UIScrollViewDelega
         webView.scrollView.addSubview(refreshControl)
         webView.scrollView.delegate = self
         webView.allowsBackForwardNavigationGestures = true
+        
+        let contentController = self.webView.configuration.userContentController
+        contentController.add(self, name: "checkProfile")
+        contentController.add(self, name: "setIsProductModalOpen")
+        contentController.add(self, name: "toggleVoiceSearch")
+        contentController.add(self, name: "blockUser")
+        contentController.add(self, name: "showAlert")
+        contentController.add(self, name: "closeProfile")
+        
+        let source: String = "var meta = document.createElement('meta');" +
+            "meta.name = 'viewport';" +
+            "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
+            "var head = document.getElementsByTagName('head')[0];" +
+            "head.appendChild(meta);" +
+        "$('#header-layout').find('.col-8').removeClass('col-8').addClass('col');" +
+        "$('#header-layout').find('.col-4').removeClass('col-4').addClass('col');"
+        let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
+        contentController.addUserScript(script)
     }
     
     override func viewWillAppear(_ animated: Bool) {
@@ -62,6 +91,12 @@ class FirstTabViewController: UIViewController, WKUIDelegate, UIScrollViewDelega
         }
         print(address)
         if let u = myURL{
+            let lang = UserDefaults.standard.string(forKey: "i18n_language")
+            var intLang = 0
+            if lang == "id" {
+                intLang = 1
+            }
+            self.webView.evaluateJavaScript("{window.localStorage.lang = \(intLang)}")
             let myRequest = URLRequest(url: u)
             webView.load(myRequest)
         }
@@ -133,14 +168,254 @@ class FirstTabViewController: UIViewController, WKUIDelegate, UIScrollViewDelega
         scrollView.pinchGestureRecognizer?.isEnabled = false
     }
 
-    /*
-    // MARK: - Navigation
+    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
+        if message.name == "checkProfile" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? String else {
+                return
+            }
+            if ViewController.checkIsChangePerson() {
+                if param2 == "like" {
+                    self.webView.evaluateJavaScript("likeProduct('\(param1)',true);")
+                } else if param2 == "comment" {
+                    self.webView.evaluateJavaScript("openComment('\(param1)',true);")
+                } else if param2 == "report_user" {
+                    self.webView.evaluateJavaScript("reportUser('\(param1)',true);")
+                } else if param2 == "report_content" {
+                    self.webView.evaluateJavaScript("reportContent('\(param1.split(separator: "|")[0])','\(param1.split(separator: "|")[1])',true);")
+                } else if param2 == "block_user" {
+                    self.webView.evaluateJavaScript("blockUser('\(param1)',true);")
+                } else if param2 == "follow_store" {
+                    self.webView.evaluateJavaScript("follow(true);")
+                } else if param2 == "homepage" {
+                    self.webView.evaluateJavaScript("window.location.href = '\(param1)';")
+                } else {
+                    self.webView.evaluateJavaScript("openNewPost(true);")
+                }
+            } else {
+                self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
+            }
+        } else if message.name == "setIsProductModalOpen" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? Bool else {
+                return
+            }
+            if param1 {
+                
+            } else {
+                
+            }
+        } else if message.name == "toggleVoiceSearch" {
+            if !isAllowSpeech {
+                setupSpeech()
+            } else {
+                runVoice()
+            }
+        } else if message.name == "blockUser" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? Bool else {
+                return
+            }
+            if param2 {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "1"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            } else {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "0"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            }
+        } else if message.name == "showAlert" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String else {
+                return
+            }
+            showToast(message: param1, controller: self.tabBarController!)
+        } else if message.name == "blockUser" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? Bool else {
+                return
+            }
+            if param2 {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "1"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            } else {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "0"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    func setupSpeech() {
+
+        self.speechRecognizer?.delegate = self
+
+        SFSpeechRecognizer.requestAuthorization { (authStatus) in
+
+            var isButtonEnabled = false
+
+            switch authStatus {
+            case .authorized:
+                isButtonEnabled = true
+
+            case .denied:
+                isButtonEnabled = false
+                print("User denied access to speech recognition")
+
+            case .restricted:
+                isButtonEnabled = false
+                print("Speech recognition restricted on this device")
+
+            case .notDetermined:
+                isButtonEnabled = false
+                print("Speech recognition not yet authorized")
+            @unknown default:
+                isButtonEnabled = false
+            }
+
+            OperationQueue.main.addOperation() {
+                self.isAllowSpeech = isButtonEnabled
+                if isButtonEnabled {
+                    UserDefaults.standard.set(isButtonEnabled, forKey: "allowSpeech")
+                    self.runVoice()
+                }
+            }
+        }
+    }
+    
+    func runVoice() {
+        if !audioEngine.isRunning {
+            alertController = UIAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
+            self.present(alertController, animated: true)
+            self.webView.evaluateJavaScript("toggleVoiceButton(true)")
+            self.startRecording()
+        }
+    }
+    
+    func startRecording() {
+
+        // Clear all previous session data and cancel task
+        if recognitionTask != nil {
+            recognitionTask?.cancel()
+            recognitionTask = nil
+        }
+
+        // Create instance of audio session to record voice
+        let audioSession = AVAudioSession.sharedInstance()
+        do {
+            try audioSession.setCategory(AVAudioSession.Category.record, mode: .default, options: [])
+            try audioSession.setMode(AVAudioSession.Mode.measurement)
+            try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
+        } catch {
+            print("audioSession properties weren't set because of an error.")
+        }
+
+        self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
+
+        let inputNode = audioEngine.inputNode
+
+        guard let recognitionRequest = recognitionRequest else {
+            fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
+        }
+
+        recognitionRequest.shouldReportPartialResults = true
+
+        self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
 
-    // In a storyboard-based application, you will often want to do a little preparation before navigation
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
-        // Get the new view controller using segue.destination.
-        // Pass the selected object to the new view controller.
+            var isFinal = false
+
+            if result != nil {
+                let text = result?.bestTranscription.formattedString
+                isFinal = (result?.isFinal)!
+                self.alertController.dismiss(animated: true)
+                self.audioEngine.stop()
+                self.recognitionRequest?.endAudio()
+                self.webView.evaluateJavaScript("toggleVoiceButton(false)")
+                self.webView.evaluateJavaScript("submitVoiceSearch('\(text ?? "")')")
+            } else {
+                self.alertController.dismiss(animated: true)
+            }
+
+            if error != nil || isFinal {
+
+                self.audioEngine.stop()
+                inputNode.removeTap(onBus: 0)
+
+                self.recognitionRequest = nil
+                self.recognitionTask = nil
+
+                self.isAllowSpeech = true
+            }
+        })
+
+        let recordingFormat = inputNode.outputFormat(forBus: 0)
+        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
+            self.recognitionRequest?.append(buffer)
+        }
+
+        self.audioEngine.prepare()
+
+        do {
+            try self.audioEngine.start()
+        } catch {
+            print("audioEngine couldn't start because of an error.")
+        }
     }
-    */
 
 }
+
+extension FirstTabViewController: SFSpeechRecognizerDelegate {
+
+    func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
+        if available {
+            self.isAllowSpeech = true
+        } else {
+            self.isAllowSpeech = false
+        }
+    }
+}

+ 11 - 4
appbuilder-ios/AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -29,12 +29,13 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         
         tableView.delegate = self
         tableView.dataSource = self
-        tableView.layoutMargins = .init(top: 0, left: 5, bottom: 0, right: 5)
-        tableView.separatorStyle = .none
+        tableView.layoutMargins = .init(top: 0, left: 0, bottom: 0, right: 0)
+        tableView.separatorColor = .gray
         
         self.navigationController?.navigationBar.topItem?.title = "Settings".localized();
         
         makeMenu()
+        tableView.reloadData()
         
         switchVibrateMode.tintColor = .gray
         switchSaveToGallery.tintColor = .gray
@@ -261,6 +262,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             content.imageProperties.maximumSize = CGSize(width: 24, height: 24)
             content.text = menu.title
             cell.accessoryView = nil
+            cell.separatorInset = UIEdgeInsets(top: .greatestFiniteMagnitude, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
             switch menu.title {
             case "Personal Information".localized():
                 cell.accessoryType = .disclosureIndicator
@@ -276,7 +278,12 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             case "Change Language".localized():
                 cell.accessoryType = .disclosureIndicator
             case "Version".localized():
-                content.secondaryText = UIApplication.appVersion
+                let accessoryButton = UIButton(type: .custom)
+                accessoryButton.setTitle(UIApplication.appVersion, for: .normal)
+                accessoryButton.setTitleColor(.black, for: .normal)
+                accessoryButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
+                accessoryButton.contentMode = .scaleAspectFit
+                cell.accessoryView = accessoryButton as UIView
             case "Vibrate Mode".localized():
                 cell.accessoryView = switchVibrateMode
             case "Save to Gallery".localized():
@@ -361,7 +368,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { (UIAlertAction) in
             }))
             
-            alert.addAction(UIAlertAction(title: "Select", style: .default, handler: { (UIAlertAction) in
+            alert.addAction(UIAlertAction(title: "Select".localized(), style: .default, handler: { (UIAlertAction) in
                 let selectedIndex = pickerView.selectedRow(inComponent: 0)
                 let lang = self.language[selectedIndex].values.first
                 UserDefaults.standard.set(lang, forKey: "i18n_language")

+ 284 - 9
appbuilder-ios/AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -7,13 +7,24 @@
 
 import UIKit
 import WebKit
+import NexilisLite
+import Speech
 
-class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKNavigationDelegate  {
+class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKNavigationDelegate, WKScriptMessageHandler  {
 
     @IBOutlet weak var webView: WKWebView!
     var address = ""
     private var lastContentOffset: CGFloat = 0
     
+    var isAllowSpeech = false
+    
+    let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "id"))
+
+    var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
+    var recognitionTask : SFSpeechRecognitionTask?
+    let audioEngine = AVAudioEngine()
+    var alertController = UIAlertController()
+    
     override func viewDidLoad() {
         super.viewDidLoad()
         let tapGesture = UITapGestureRecognizer(target: self, action: #selector(collapseDocked))
@@ -26,7 +37,24 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         webView.scrollView.delegate = self
         webView.navigationDelegate = self
         webView.allowsBackForwardNavigationGestures = true
-        // Do any additional setup after loading the view.
+        
+        let contentController = self.webView.configuration.userContentController
+        contentController.add(self, name: "checkProfile")
+        contentController.add(self, name: "setIsProductModalOpen")
+        contentController.add(self, name: "toggleVoiceSearch")
+        contentController.add(self, name: "blockUser")
+        contentController.add(self, name: "showAlert")
+        contentController.add(self, name: "closeProfile")
+        
+        let source: String = "var meta = document.createElement('meta');" +
+            "meta.name = 'viewport';" +
+            "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
+            "var head = document.getElementsByTagName('head')[0];" +
+            "head.appendChild(meta);" +
+        "$('#header-layout').find('.col-8').removeClass('col-8').addClass('col');" +
+        "$('#header-layout').find('.col-4').removeClass('col-4').addClass('col');"
+        let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
+        contentController.addUserScript(script)
     }
     
     override func viewWillAppear(_ animated: Bool) {
@@ -60,6 +88,12 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         }
         print(address)
         if let u = myURL{
+            let lang = UserDefaults.standard.string(forKey: "i18n_language")
+            var intLang = 0
+            if lang == "id" {
+                intLang = 1
+            }
+            self.webView.evaluateJavaScript("{window.localStorage.lang = \(intLang)}")
             let myRequest = URLRequest(url: u)
             webView.load(myRequest)
         }
@@ -130,14 +164,255 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
     }
 
 
-    /*
-    // MARK: - Navigation
+    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
+        print("KACAU \(message.name)")
+        if message.name == "checkProfile" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? String else {
+                return
+            }
+            if ViewController.checkIsChangePerson() {
+                if param2 == "like" {
+                    self.webView.evaluateJavaScript("likeProduct('\(param1)',true);")
+                } else if param2 == "comment" {
+                    self.webView.evaluateJavaScript("openComment('\(param1)',true);")
+                } else if param2 == "report_user" {
+                    self.webView.evaluateJavaScript("reportUser('\(param1)',true);")
+                } else if param2 == "report_content" {
+                    self.webView.evaluateJavaScript("reportContent('\(param1.split(separator: "|")[0])','\(param1.split(separator: "|")[1])',true);")
+                } else if param2 == "block_user" {
+                    self.webView.evaluateJavaScript("blockUser('\(param1)',true);")
+                } else if param2 == "follow_store" {
+                    self.webView.evaluateJavaScript("follow(true);")
+                } else if param2 == "homepage" {
+                    self.webView.evaluateJavaScript("window.location.href = '\(param1)';")
+                } else {
+                    self.webView.evaluateJavaScript("openNewPost(true);")
+                }
+            } else {
+                self.webView.evaluateJavaScript("{if(pauseAll){pauseAll();}}")
+            }
+        } else if message.name == "setIsProductModalOpen" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? Bool else {
+                return
+            }
+            if param1 {
+                
+            } else {
+                
+            }
+        } else if message.name == "toggleVoiceSearch" {
+            if !isAllowSpeech {
+                setupSpeech()
+            } else {
+                runVoice()
+            }
+        } else if message.name == "blockUser" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? Bool else {
+                return
+            }
+            if param2 {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "1"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            } else {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "0"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            }
+        } else if message.name == "showAlert" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String else {
+                return
+            }
+            showToast(message: param1, controller: self.tabBarController!)
+        } else if message.name == "blockUser" {
+            guard let dict = message.body as? [String: AnyObject],
+                  let param1 = dict["param1"] as? String,
+                  let param2 = dict["param2"] as? Bool else {
+                return
+            }
+            if param2 {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "1"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            } else {
+                DispatchQueue.global().async {
+                    if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getUnBlock(l_pin: param1)) {
+                        if response.isOk() {
+                            DispatchQueue.main.async {
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: [
+                                        "ex_block" : "0"
+                                    ], _where: "f_pin = '\(param1)'")
+                                })
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    func setupSpeech() {
+
+        self.speechRecognizer?.delegate = self
+
+        SFSpeechRecognizer.requestAuthorization { (authStatus) in
+
+            var isButtonEnabled = false
+
+            switch authStatus {
+            case .authorized:
+                isButtonEnabled = true
+
+            case .denied:
+                isButtonEnabled = false
+                print("User denied access to speech recognition")
+
+            case .restricted:
+                isButtonEnabled = false
+                print("Speech recognition restricted on this device")
+
+            case .notDetermined:
+                isButtonEnabled = false
+                print("Speech recognition not yet authorized")
+            @unknown default:
+                isButtonEnabled = false
+            }
+
+            OperationQueue.main.addOperation() {
+                self.isAllowSpeech = isButtonEnabled
+                if isButtonEnabled {
+                    UserDefaults.standard.set(isButtonEnabled, forKey: "allowSpeech")
+                    self.runVoice()
+                }
+            }
+        }
+    }
+    
+    func runVoice() {
+        if !audioEngine.isRunning {
+            alertController = UIAlertController(title: "Start Recording".localized(), message: "Say something, I'm listening!".localized(), preferredStyle: .alert)
+            self.present(alertController, animated: true)
+            self.webView.evaluateJavaScript("toggleVoiceButton(true)")
+            self.startRecording()
+        }
+    }
+    
+    func startRecording() {
+
+        // Clear all previous session data and cancel task
+        if recognitionTask != nil {
+            recognitionTask?.cancel()
+            recognitionTask = nil
+        }
+
+        // Create instance of audio session to record voice
+        let audioSession = AVAudioSession.sharedInstance()
+        do {
+            try audioSession.setCategory(AVAudioSession.Category.record, mode: .default, options: [])
+            try audioSession.setMode(AVAudioSession.Mode.measurement)
+            try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
+        } catch {
+            print("audioSession properties weren't set because of an error.")
+        }
+
+        self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
+
+        let inputNode = audioEngine.inputNode
+
+        guard let recognitionRequest = recognitionRequest else {
+            fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
+        }
+
+        recognitionRequest.shouldReportPartialResults = true
+
+        self.recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
 
-    // In a storyboard-based application, you will often want to do a little preparation before navigation
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
-        // Get the new view controller using segue.destination.
-        // Pass the selected object to the new view controller.
+            var isFinal = false
+
+            if result != nil {
+                let text = result?.bestTranscription.formattedString
+                isFinal = (result?.isFinal)!
+                self.alertController.dismiss(animated: true)
+                self.audioEngine.stop()
+                self.recognitionRequest?.endAudio()
+                self.webView.evaluateJavaScript("toggleVoiceButton(false)")
+                self.webView.evaluateJavaScript("submitVoiceSearch('\(text ?? "")')")
+            } else {
+                self.alertController.dismiss(animated: true)
+            }
+
+            if error != nil || isFinal {
+
+                self.audioEngine.stop()
+                inputNode.removeTap(onBus: 0)
+
+                self.recognitionRequest = nil
+                self.recognitionTask = nil
+
+                self.isAllowSpeech = true
+            }
+        })
+
+        let recordingFormat = inputNode.outputFormat(forBus: 0)
+        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
+            self.recognitionRequest?.append(buffer)
+        }
+
+        self.audioEngine.prepare()
+
+        do {
+            try self.audioEngine.start()
+        } catch {
+            print("audioEngine couldn't start because of an error.")
+        }
     }
-    */
 
 }
+
+extension ThirdTabViewController: SFSpeechRecognizerDelegate {
+
+    func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
+        if available {
+            self.isAllowSpeech = true
+        } else {
+            self.isAllowSpeech = false
+        }
+    }
+}

+ 5 - 0
appbuilder-ios/NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings

@@ -70,6 +70,9 @@
 "Car" = "Mobil";
 "Motorbike" = "Motor";
 "eBike" = "Sepeda";
+"Login as Admin / Internal" = "Masuk sebagai Admin / Internal";
+"Change Admin / Internal Password" = "Ganti Sandi Admin / Internal";
+"Change Device" = "Ganti Perangkat";
 "Change Language" = "Ubah Bahasa";
 "Incoming Message(s)" = "Pesan Masuk";
 "Incoming Call(s)" = "Panggilan Masuk";
@@ -96,3 +99,5 @@
 "Scan Gaspol QR" = "Memindai Gaspol QR";
 "Scan QR Code" = "Memindai kode QR";
 "To use Gaspol Web, go to gaspol.co.id on your computer." = "Untuk dapat menggunakan Gaspol Web, pergi ke halaman gaspol.co.id pada komputer anda.";
+"Select" = "Pilih";
+"Select Language" = "Pilih Bahasa";