refactor(ios/engine): ResourceInfoView support for lexical models

This commit is contained in:
jahorton 2020-07-30 15:50:40 +07:00
parent 2113c2e97b
commit 01351ababb
6 changed files with 64 additions and 236 deletions

View file

@ -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 = "<group>"; };
9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.swift; sourceTree = "<group>"; };
9A3E832422EAC14A00D22D2A /* KeyboardSwitcherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSwitcherViewController.swift; sourceTree = "<group>"; };
9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelInfoViewController.swift; sourceTree = "<group>"; };
9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelAPICall.swift; sourceTree = "<group>"; };
9A60763C22892485003BCFBA /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Settings.storyboard; path = Settings/Settings.storyboard; sourceTree = "<group>"; };
9A60764322893A4E003BCFBA /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SettingsViewController.swift; path = Settings/SettingsViewController.swift; sourceTree = "<group>"; };
@ -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 */,

View file

@ -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)
}
}
}

View file

@ -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)
}

View file

@ -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<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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<Resource: LanguageResource>: UIViewController,
])
}
infoArray.append([
"title": NSLocalizedString("command-uninstall-keyboard", bundle: engineBundle, comment: ""),
"title": NSLocalizedString(uninstallLabelKey, bundle: engineBundle, comment: ""),
"subtitle": ""
])
@ -71,8 +77,7 @@ class ResourceInfoViewController<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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<Resource: LanguageResource>: 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)
}
}
}

View file

@ -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 }

View file

@ -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!")
}
}
}
/*