diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift new file mode 100644 index 0000000000..2a51dab7c3 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.swift @@ -0,0 +1,208 @@ +// +// LanguageLMDetailViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 7/19/19. +// Copyright © 2019 SIL International. All rights reserved. +// + + +import UIKit + +private let toolbarButtonTag = 100 +private let toolbarLabelTag = 101 +private let toolbarActivityIndicatorTag = 102 + +class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate { + private var userLexicalModels: [String: InstallableLexicalModel] = [:] + private var isUpdate = false + private let language: Language + public var lexicalModels: [LexicalModel]? = nil + + private var lexicalModelDownloadStartedObserver: NotificationObserver? + private var lexicalModelDownloadFailedObserver: NotificationObserver? + + init(language: Language) { + self.language = language + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + super.loadView() + loadUserLexicalModels() + tableView.dataSource = self + } + + override func viewDidLoad() { + super.viewDidLoad() + lexicalModelDownloadStartedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadStarted, + observer: self, + function: LanguageLMDetailViewController.lexicalModelDownloadStarted) + lexicalModelDownloadFailedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadFailed, + observer: self, + function: LanguageLMDetailViewController.lexicalModelDownloadFailed) + log.info("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)") + } + + override open func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + log.info("didAppear: LanguageLMDetailViewController (actually willAppear)") + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + log.info("didAppear: LanguageLMDetailViewController") + + navigationController?.setToolbarHidden(true, animated: true) + } + + // MARK: - Table view data source UITableViewDataSource + + override func numberOfSections(in tableView: UITableView) -> Int { + return lexicalModels!.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellIdentifier = "Cell" + if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { + return cell + } + + let cell = KeyboardNameTableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 204.0 / 255.0, green: 136.0 / 255.0, blue: 34.0 / 255.0, alpha: 1.0) + cell.selectedBackgroundView = selectionColor + return cell + } + + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + let lexicalModel = lexicalModels![indexPath.section] + let cell = cell as! KeyboardNameTableViewCell + cell.indexPath = indexPath + cell.textLabel?.text = lexicalModel.name + if isAdded(languageID: language.id, lexicalModelID: lexicalModel.id) { + cell.accessoryType = .checkmark + cell.isUserInteractionEnabled = false + cell.textLabel?.isEnabled = false + cell.detailTextLabel?.isEnabled = false + } else { + cell.accessoryType = .none + cell.isUserInteractionEnabled = true + cell.textLabel?.isEnabled = true + cell.detailTextLabel?.isEnabled = true + } + + let kbState = Manager.shared.stateForLexicalModel(withID: lexicalModel.id) + cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.cellForRow(at: indexPath)?.isSelected = false + let lexicalModelIndex = indexPath.section + let lexicalModel = lexicalModels![lexicalModelIndex] + + let state = Manager.shared.stateForLexicalModel(withID: lexicalModel.id) + if state != .downloading { + if state == .needsDownload { + isUpdate = false + } else { + isUpdate = true + } + + let alertController = UIAlertController(title: "\(language.name): \(lexicalModel.name)", + message: "Would you like to download this dictionary?", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "Cancel", + style: UIAlertActionStyle.cancel, + handler: nil)) + alertController.addAction(UIAlertAction(title: "Download", + style: UIAlertActionStyle.default, + handler: {_ in self.downloadHandler(lexicalModelIndex)} )) + + self.present(alertController, animated: true, completion: nil) + } + } + + func downloadHandler(_ lexicalModelIndex: Int) { +// Manager.shared.downloadLexicalModel(withID: lexicalModels![lexicalModelIndex].id, +// languageID: language.id, isUpdate: isUpdate) + Manager.shared.downloadLexicalModelPackage(string: (lexicalModels?[lexicalModelIndex].packageFilename)!) + /* + if let packageURL = URLComponents(string: (lexicalModels?[lexicalModelIndex].packageFilename)!) { + Manager.shared.downloadLexicalModel(from: packageURL.url!) + } else { + log.error("couldn't make url to download \(lexicalModels?[lexicalModelIndex].packageFilename)") + } + */ + } + + private func lexicalModelDownloadStarted() { + log.info("lexicalModelDownloadStarted: LanguageLMDetailViewController") + view.isUserInteractionEnabled = false + navigationItem.setHidesBackButton(true, animated: true) + + let toolbarFrame = navigationController!.toolbar.frame + let labelFrame = CGRect(origin: toolbarFrame.origin, + size: CGSize(width: toolbarFrame.width * 0.95, height: toolbarFrame.height * 0.7)) + let label = UILabel(frame: labelFrame) + label.backgroundColor = UIColor.clear + label.textColor = UIColor.white + label.textAlignment = .center + label.center = CGPoint(x: toolbarFrame.width * 0.5, y: toolbarFrame.height * 0.5) + label.text = "Downloading\u{2026}" + label.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, + .flexibleBottomMargin, .flexibleWidth, .flexibleHeight] + label.tag = toolbarLabelTag + + let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray) + indicatorView.center = CGPoint(x: toolbarFrame.width - indicatorView.frame.width, + y: toolbarFrame.height * 0.5) + indicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin] + indicatorView.tag = toolbarActivityIndicatorTag + indicatorView.startAnimating() + + navigationController?.toolbar.viewWithTag(toolbarButtonTag)?.removeFromSuperview() + navigationController?.toolbar.viewWithTag(toolbarLabelTag)?.removeFromSuperview() + navigationController?.toolbar.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() + navigationController?.toolbar.addSubview(label) + navigationController?.toolbar.addSubview(indicatorView) + navigationController?.setToolbarHidden(false, animated: true) + } + + private func lexicalModelDownloadFailed() { + log.info("lexicalModelDownloadFailed: LanguageLMDetailViewController") + view.isUserInteractionEnabled = true + navigationItem.setHidesBackButton(false, animated: true) + } + + private func loadUserLexicalModels() { + guard let userLmList = Storage.active.userDefaults.userLexicalModels, !userLmList.isEmpty else { + userLexicalModels = [:] + return + } + + userLexicalModels = [:] + for lm in userLmList { + let dictKey = "\(lm.languageID)_\(lm.id)" + userLexicalModels[dictKey] = lm + } + } + + private func isAdded(languageID: String?, lexicalModelID: String?) -> Bool { + guard let languageID = languageID, let lexicalModelID = lexicalModelID else { + return false + } + return userLexicalModels["\(languageID)_\(lexicalModelID)"] != nil + } +} diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.xib b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.xib new file mode 100644 index 0000000000..d379dc8b53 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageLMDetailViewController.xib @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index fd169b54a8..f354001334 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -15,7 +15,7 @@ private let toolbarActivityIndicatorTag = 102 class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelegate { private var userLexicalModels: [InstallableLexicalModel] = [InstallableLexicalModel]() - public var language: Language? = nil + public var language: Language! private var updateQueue: [InstallableLexicalModel]? private var _isDoneButtonEnabled = false private var isDidUpdateCheck = false @@ -24,17 +24,19 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega private var lexicalModelDownloadCompletedObserver: NotificationObserver? private var lexicalModelDownloadFailedObserver: NotificationObserver? + public init(_ language: Language) { + self.language = language + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() - if let langName = language?.name { - title = "\(langName) Lexical Models" - } else { - title = "Lexical Models" - } - if language == nil { - setIsDoneButtonEnabled(false) - } + title = "\(language.name) Dictionaries" isDidUpdateCheck = false updateQueue = nil if Manager.shared.canAddNewLexicalModels { @@ -137,9 +139,6 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega if Manager.shared.removeLexicalModel(at: globalIndex) { loadUserLexicalModels() } - if language == nil { - setIsDoneButtonEnabled(true) - } } override func tableView(_ tableView: UITableView, @@ -148,9 +147,6 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } func showLexicalModelInfoView(with index: Int) { - if language == nil { - setIsDoneButtonEnabled(true) - } let lm = userLexicalModels[index] let version = lm.version @@ -238,14 +234,14 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } updateQueue = nil let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel - label?.text = "Lexical models successfully updated!" + label?.text = "Dictionaries successfully updated!" navigationController?.toolbar?.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed), userInfo: nil, repeats: false) } } else { let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel - label?.text = "Lexical model successfully downloaded!" + label?.text = "Dictionary successfully downloaded!" navigationController?.toolbar?.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed), userInfo: nil, repeats: false) @@ -276,9 +272,9 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega let title: String if view == navigationController?.topViewController?.view { updateQueue = nil - title = "Lexical model Update Error" + title = "Dictionary Update Error" } else { - title = "Lexical model Download Error" + title = "Dictionary Download Error" } navigationController?.setToolbarHidden(true, animated: true) @@ -421,13 +417,30 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } func showAddLexicalModel() { - let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) - button?.isEnabled = false - let vc = LanguageViewController(Manager.shared.apiLexicalModelRepository) //may need to be different for models - navigationController?.pushViewController(vc, animated: true) - if language == nil { - setIsDoneButtonEnabled(true) + //get list of lexical models for this languageID and show it + func listCompletionHandler(lexicalModels: [LexicalModel]?, error: Error?) -> Void { + if let error = error { + log.info("Failed to fetch lexical model list for "+language.id+". error: "+(error as! String)) + self.lexicalModelDownloadFailed(LexicalModelDownloadFailedNotification(lmOrLanguageID: language.id, error: error)) + } else if nil == lexicalModels { + log.info("No lexical models available for language \(language.id) (nil)") + } else if 0 == lexicalModels?.count { + log.info("No lexical models available for language \(language.id) (empty)") + } else { + log.info("Fetched lexical model list for "+language.id+".") + // show the list of lexical models (on the main thread) + DispatchQueue.main.async { + let button: UIButton? = (self.navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) + button?.isEnabled = false + let vc = LanguageLMDetailViewController(language: self.language) + vc.lexicalModels = lexicalModels! + self.navigationController?.pushViewController(vc, animated: true) + } + } } + + Manager.shared.apiLexicalModelRepository.fetchList(languageID: language.id, completionHandler: listCompletionHandler) } + }