diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 230900f813..a5900e2d8c 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -49,7 +49,6 @@ 9A0FCA0922D7C58B00D33F86 /* Keyman.bundle in Resources */ = {isa = PBXBuildFile; fileRef = F27FCB51157FE3CE00FBBA20 /* Keyman.bundle */; }; 9A3B14D2229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */; }; 9A3E832522EAC14A00D22D2A /* KeyboardSwitcherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3E832422EAC14A00D22D2A /* KeyboardSwitcherViewController.swift */; }; - 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; 9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; }; 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; @@ -387,7 +386,6 @@ 9A0FC9FC22D66D9E00D33F86 /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = ../../Carthage/Build/iOS/Reachability.framework; sourceTree = ""; }; 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.swift; sourceTree = ""; }; 9A3E832422EAC14A00D22D2A /* KeyboardSwitcherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSwitcherViewController.swift; sourceTree = ""; }; - 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelInfoViewController.swift; sourceTree = ""; }; 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelAPICall.swift; sourceTree = ""; }; 9A60763C22892485003BCFBA /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Settings.storyboard; path = Settings/Settings.storyboard; sourceTree = ""; }; 9A60764322893A4E003BCFBA /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingsViewController.swift; path = Settings/SettingsViewController.swift; sourceTree = ""; }; @@ -1033,7 +1031,6 @@ children = ( CE754A0323D162E90030CB79 /* ResourceInfoViewController.swift */, CEA14872240780E100C6ECD2 /* ResourceInfoView.xib */, - 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */, 9A3E832422EAC14A00D22D2A /* KeyboardSwitcherViewController.swift */, 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */, C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */, @@ -1507,7 +1504,6 @@ C024C9961FA6EC650060583B /* NotificationCenter+Typed.swift in Sources */, C06D37361F81F5C300F61AE0 /* KeyboardMenuView.swift in Sources */, CE67D961228A6F190029F2B5 /* KeyboardCommandStructs.swift in Sources */, - 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */, CE87751E24C68DA500B1475A /* KeyboardSearchViewController.swift in Sources */, CE8B0BBF248764ED0045EB2E /* KMPResource.swift in Sources */, 9AD4F53C229F85AC007992D3 /* LanguageSettingsViewController.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift deleted file mode 100644 index 86c5774c74..0000000000 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// LexicalModelInfoViewController.swift -// KeymanEngine -// -// Created by Randy Boring on 3/19/19. -// Copyright © 2019 SIL International. All rights reserved. -// -import UIKit -import Foundation - -class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate { - var lexicalModelCount: Int = 0 - var lexicalModelIndex: Int = 0 - var lexicalModelID: String = "" - var languageID: String = "" - var lexicalModelVersion: String = "" - var lexicalModelCopyright: String = "" - var isCustomLexicalModel: Bool = false - - private var infoArray = [[String: String]]() - - override func viewDidLoad() { - super.viewDidLoad() - - infoArray = [[String: String]]() - infoArray.append([ - "title": NSLocalizedString("info-label-version-lexical-model", bundle: engineBundle, comment: ""), - "subtitle": lexicalModelVersion - ]) - - if !isCustomLexicalModel { - infoArray.append([ - "title": NSLocalizedString("info-command-help", bundle: engineBundle, comment: ""), - "subtitle": "" - ]) - } - infoArray.append([ - "title": NSLocalizedString("command-uninstall-lexical-model", bundle: engineBundle, comment: ""), - "subtitle": "" - ]) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - navigationController?.setToolbarHidden(true, animated: true) - log.info("didAppear: LexicalModelInfoViewController") - } - - override func numberOfSections(in tableView: UITableView) -> Int { - return 1 - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if isCustomLexicalModel { - return 2 - } else { - return 3 - } - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cellIdentifier = "Cell" - if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { - return cell - } - return UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if !isCustomLexicalModel { - if indexPath.row == 1 { - let url = URL(string: "\(KeymanHosts.HELP_KEYMAN_COM)/lexicalModel/\(lexicalModelID)/\(lexicalModelVersion)/")! - if let openURL = Manager.shared.openURL { - _ = openURL(url) - } else { - log.error("openURL not set in Manager. Failed to open \(url)") - } - } else if indexPath.row == 2 { - showDeleteLexicalModel() - } - } else if indexPath.row == 1 { - showDeleteLexicalModel() - } - } - - private func fetchedLexicalModelData(_ data: Data) { - guard let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [AnyHashable: Any] else { - return - } - let lexicalModels = json[Key.language] as? [Any] - let kbDict = lexicalModels?[0] as? [AnyHashable: Any] - var info = infoArray[1] - let copyright = kbDict?[Key.lexicalModelCopyright] as? String ?? "Unknown" - - info["subtitle"] = copyright - infoArray[1] = info - tableView.reloadData() - } - - override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - cell.selectionStyle = .none - cell.accessoryType = .none - cell.textLabel?.text = infoArray[indexPath.row]["title"] - cell.detailTextLabel?.text = infoArray[indexPath.row]["subtitle"] - cell.tag = indexPath.row - - if !isCustomLexicalModel { - if indexPath.row == 1 { - cell.accessoryType = .disclosureIndicator - } - } - } - - private func showDeleteLexicalModel() { - let helpText = NSLocalizedString("command-uninstall-lexical-model-confirm", bundle: engineBundle, comment: "") - let alertController = UIAlertController(title: title ?? "", message: helpText, - preferredStyle: UIAlertController.Style.alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("command-cancel", bundle: engineBundle, comment: ""), - style: UIAlertAction.Style.cancel, - handler: nil)) - alertController.addAction(UIAlertAction(title: NSLocalizedString("command-uninstall", bundle: engineBundle, comment: ""), - style: UIAlertAction.Style.default, - handler: deleteHandler)) - - self.present(alertController, animated: true, completion: nil) - } - - func deleteHandler(withAction action: UIAlertAction) { - if Manager.shared.removeLexicalModel(at: lexicalModelIndex) { - navigationController?.popToRootViewController(animated: true) - } - } -} diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index a57fa75609..af92ff2679 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -96,8 +96,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega cell.selectedBackgroundView = selectionColor return cell } - - // TODO: Refactor. Duplicated in LexicalModelInfoViewController + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { if !Manager.shared.canRemoveLexicalModels { return false @@ -125,16 +124,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega func showLexicalModelInfoView(with index: Int) { let lm = userLexicalModels[index] - let version = lm.version - - let infoView = LexicalModelInfoViewController() - infoView.title = lm.name - infoView.lexicalModelCount = userLexicalModels.count - infoView.lexicalModelIndex = local2globalIndex(index) - infoView.lexicalModelID = lm.id - infoView.languageID = lm.languageID - infoView.lexicalModelVersion = version - infoView.isCustomLexicalModel = lm.isCustom + + let infoView = ResourceInfoViewController(for: lm, mayDelete: Manager.shared.canRemoveLexicalModels) navigationController?.pushViewController(infoView, animated: true) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift index 869af0c2c3..4f230797f3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/ResourceInfoViewController.swift @@ -9,22 +9,15 @@ import Foundation import UIKit -/** - * At present, this class only supports keyboard resources. The base design is partially refactored to - * eventually support lexical model resources as well, but additional work is needed before this class - * will be ready... possibly as a common base class. - */ class ResourceInfoViewController: UIViewController, UIAlertViewDelegate, UITableViewDelegate, UITableViewDataSource { - // Collectively used to determine if a keyboard may be deleted. - var keyboardCount: Int = 0 - var keyboardIndex: Int = 0 - var isCustomKeyboard: Bool = false // also used to toggle QR code gen + display - - var keyboardCopyright: String = "" + var isCustomKeyboard: Bool = false // legacy; was also used to toggle QR code gen + display + // The data backing our UI text in the UITableView. private var infoArray = [[String: String]]() let resource: Resource + let package: Resource.Package? + let mayDelete: Bool @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var contentView: UIView! @@ -35,8 +28,10 @@ class ResourceInfoViewController: UIViewController, @IBOutlet weak var tableHeightConstraint: NSLayoutConstraint! @IBOutlet weak var labelHeightConstraint: NSLayoutConstraint! - init(for resource: Resource) { + init(for resource: Resource, mayDelete: Bool = false) { self.resource = resource + self.package = ResourceFileManager.shared.getInstalledPackage(for: resource) + self.mayDelete = mayDelete super.init(nibName: "ResourceInfoView", bundle: Bundle.init(for: ResourceInfoViewController.self)) } @@ -48,9 +43,20 @@ class ResourceInfoViewController: UIViewController, override func viewDidLoad() { super.viewDidLoad() + var versionLabelKey: String + var uninstallLabelKey: String + + if Resource.self == InstallableLexicalModel.self { + versionLabelKey = "info-label-version-lexical-model" + uninstallLabelKey = "command-uninstall-lexical-model" + } else { + versionLabelKey = "info-label-version-keyboard" + uninstallLabelKey = "command-uninstall-keyboard" + } + infoArray = [[String: String]]() infoArray.append([ - "title": NSLocalizedString("info-label-version-keyboard", bundle: engineBundle, comment: ""), + "title": NSLocalizedString(versionLabelKey, bundle: engineBundle, comment: ""), "subtitle": resource.version ]) @@ -61,7 +67,7 @@ class ResourceInfoViewController: UIViewController, ]) } infoArray.append([ - "title": NSLocalizedString("command-uninstall-keyboard", bundle: engineBundle, comment: ""), + "title": NSLocalizedString(uninstallLabelKey, bundle: engineBundle, comment: ""), "subtitle": "" ]) @@ -71,8 +77,7 @@ class ResourceInfoViewController: UIViewController, tableView.reloadData() // Generate & display the QR code! - let package = ResourceFileManager.shared.getInstalledPackage(for: resource)! - if package.distributionMethod == .cloud { + if package?.distributionMethod ?? .unknown == .cloud { if let resourceURL = resource.sharableURL { if let qrImg = generateQRCode(from: resourceURL) { qrImageView.image = qrImg @@ -86,6 +91,8 @@ class ResourceInfoViewController: UIViewController, } else { shareLabel.isHidden = true } + + self.title = resource.name } // Should be supported in iOS 7+. We only support 9+, so we should be fine here. @@ -141,27 +148,13 @@ class ResourceInfoViewController: UIViewController, log.error("openURL not set in Manager. Failed to open \(url)") } } else if indexPath.row == 2 { - showDeleteKeyboard() + showDeleteResource() } } else if indexPath.row == 1 { - showDeleteKeyboard() + showDeleteResource() } } - private func fetchedKeyboardData(_ data: Data) { - guard let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [AnyHashable: Any] else { - return - } - let keyboards = json[Key.language] as? [Any] - let kbDict = keyboards?[0] as? [AnyHashable: Any] - var info = infoArray[1] - let copyright = kbDict?[Key.keyboardCopyright] as? String ?? "Unknown" - - info["subtitle"] = copyright - infoArray[1] = info - tableView.reloadData() - } - func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.selectionStyle = .none cell.accessoryType = .none @@ -172,35 +165,28 @@ class ResourceInfoViewController: UIViewController, if !isCustomKeyboard { if indexPath.row == 1 { cell.accessoryType = .disclosureIndicator - } else if indexPath.row == 2 && !canDeleteKeyboard { + } else if indexPath.row == 2 && !mayDelete { cell.isUserInteractionEnabled = false cell.textLabel?.isEnabled = false cell.detailTextLabel?.isEnabled = false } - } else if indexPath.row == 1 && !canDeleteKeyboard { + } else if indexPath.row == 1 && !mayDelete { cell.isUserInteractionEnabled = false cell.textLabel?.isEnabled = false cell.detailTextLabel?.isEnabled = false } } - private var canDeleteKeyboard: Bool { - if !Manager.shared.canRemoveKeyboards { - return false + private func showDeleteResource() { + var uninstallLabelKey: String + + if Resource.self == InstallableLexicalModel.self { + uninstallLabelKey = "command-uninstall-lexical-model-confirm" + } else { + uninstallLabelKey = "command-uninstall-keyboard-confirm" } - if !Manager.shared.canRemoveDefaultKeyboard { - return keyboardIndex != 0 - } - - if keyboardIndex > 0 { - return true - } - return keyboardCount > 1 - } - - private func showDeleteKeyboard() { - let uninstallHelp = NSLocalizedString("command-uninstall-keyboard-confirm", bundle: engineBundle, comment: "") + let uninstallHelp = NSLocalizedString(uninstallLabelKey, bundle: engineBundle, comment: "") let alertController = UIAlertController(title: title ?? "", message: uninstallHelp, preferredStyle: UIAlertController.Style.alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("command-cancel", bundle: engineBundle, comment: ""), @@ -214,8 +200,14 @@ class ResourceInfoViewController: UIViewController, } func deleteHandler(withAction action: UIAlertAction) { - if Manager.shared.removeKeyboard(at: keyboardIndex) { + if let lexicalModel = resource as? InstallableLexicalModel { + if Manager.shared.removeLexicalModel(withFullID: lexicalModel.typedFullID) { navigationController?.popToRootViewController(animated: true) + } + } else if let keyboard = resource as? InstallableKeyboard { + if Manager.shared.removeKeyboard(withFullID: keyboard.typedFullID) { + navigationController?.popToRootViewController(animated: true) + } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Model/LanguageResource.swift b/ios/engine/KMEI/KeymanEngine/Classes/Model/LanguageResource.swift index 6b242189c9..9df571eb0a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Model/LanguageResource.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Model/LanguageResource.swift @@ -56,6 +56,7 @@ public protocol LanguageResourceFullID: AnyLanguageResourceFullID { // specific LanguageResources use. public protocol AnyLanguageResource { var id: String { get } + var name: String { get } var languageID: String { get } // Was not always tracked within KeymanEngine - is optional for legacy reasons. var packageID: String? { get } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index fa4eceaa45..217938828b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -362,10 +362,8 @@ class LanguageSettingsViewController: UITableViewController { } let kbIndex:Int = index let thisKb = globalUserKeyboards[kbIndex] - let infoView = ResourceInfoViewController(for: thisKb) - infoView.title = thisKb.name - infoView.keyboardCount = globalUserKeyboards.count - infoView.keyboardIndex = index + let mayDelete = mayDeleteKeyboard(keyboardIndex: index, keyboardCount: globalUserKeyboards.count) + let infoView = ResourceInfoViewController(for: thisKb, mayDelete: mayDelete) infoView.isCustomKeyboard = thisKb.isCustom navigationController?.pushViewController(infoView, animated: true) } else { @@ -373,44 +371,27 @@ class LanguageSettingsViewController: UITableViewController { return } } - + + private func mayDeleteKeyboard(keyboardIndex: Int, keyboardCount: Int) -> Bool { + if !Manager.shared.canRemoveKeyboards { + return false + } + + if !Manager.shared.canRemoveDefaultKeyboard { + return keyboardIndex != 0 + } + + if keyboardIndex > 0 { + return true + } + return keyboardCount > 1 + } + func showLexicalModelsView() { //LanguageLexicalModelPickerViewController? (should show just the models for this language) let lmListView = LexicalModelPickerViewController(self.language) lmListView.language = self.language navigationController?.pushViewController(lmListView, animated: true) - } - - func showLexicalModelInfoView() { - if let lm = language.lexicalModels?[safe: 0] { - let version = lm.version - let matchingFullID = FullLexicalModelID(lexicalModelID: lm.id, languageID: language.id) - - let userData = Storage.active.userDefaults - - if let globalUserLexicalModels = userData.userLexicalModels { - if let index = globalUserLexicalModels.firstIndex(where: { $0.fullID == matchingFullID }) { - guard index < globalUserLexicalModels.count else { - return - } - let lmIndex:Int = index - let thisLm = globalUserLexicalModels[lmIndex] - let infoView = LexicalModelInfoViewController() - infoView.title = thisLm.name - infoView.lexicalModelCount = globalUserLexicalModels.count - infoView.lexicalModelIndex = index - infoView.lexicalModelID = thisLm.id - infoView.languageID = language.id - infoView.lexicalModelVersion = version ?? InstallableConstants.defaultVersion - infoView.isCustomLexicalModel = thisLm.isCustom - navigationController?.pushViewController(infoView, animated: true) - } else { - log.error("this lexical model \(matchingFullID) not found among language's installed lexical model!") - } - } else { - log.error("no lexical models in the global models list!") - } - } } /*