From 30b04261cf52003c1dba98a1b0832be64114a3b1 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 9 May 2019 19:03:54 -0500 Subject: [PATCH 01/47] start of UI for lexical models begin with top menu, stub for Settings, and changing the underlying key for showing Get Started --- .../KMEI/KeymanEngine/Classes/Manager.swift | 27 +++++++++++++++++++ .../GetStartedViewController.swift | 24 ++++++++++++++--- .../Keyman/Keyman/MainViewController.swift | 22 +++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index fe2cf47f2c..7c9818fb79 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -215,6 +215,33 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management + public func manageLanguages(inVC: UIViewController) -> Void { +// let languagesVC = LanguageViewController(Manager.shared.apiKeyboardRepository) //later Languages +// let rep = Manager.shared.apiKeyboardRepository +// let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) +// navigationController?.pushViewController(languagesVC, animated: true) + +// textView.isUserInteractionEnabled = false +// if UIDevice.current.userInterfaceIdiom == .phone { +// present(languagesVC, animated: true, completion: nil) +// } else { + let alertController = UIAlertController(title: "Languages Settings", message: "Languages installed", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "Cancel", + style: UIAlertActionStyle.cancel, + handler: nil)) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.default, + handler: nil)) + + inVC.present(alertController, animated: true, completion: nil) + +// let popover = UIPopoverController(contentViewController: languagesVC) +// popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) +// (from: (navigationItem.rightBarButtonItems?[8])!, permittedArrowDirections: .any, animated: true) +// } + } + /// Sets the current keyboard, querying from the user's list of keyboards. /// /// - Precondition: diff --git a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift index 24cc430fea..9c6033570f 100644 --- a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift +++ b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift @@ -179,13 +179,31 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi @objc func switchValueChanged(_ sender: Any) { let userData = AppDelegate.activeUserDefaults() if let toggle = sender as? UISwitch { - userData.set(toggle.isOn, forKey: dontShowGetStartedKey) + userData.set(toggle.isOn, forKey: shouldShowGetStartedKey) userData.synchronize() } } - + private var dontShowGetStarted: Bool { + return !shouldShowGetStarted + } + + private var shouldShowGetStarted: Bool { let userData = AppDelegate.activeUserDefaults() - return userData.bool(forKey: dontShowGetStartedKey) + var value: Any? = userData.object(forKey: shouldShowGetStartedKey) + if nil == value { // new key not present, try the old key + value = userData.object(forKey: dontShowGetStartedKey) + if nil == value { + userData.set(true, forKey: shouldShowGetStartedKey) + return true // default to showing when no preference has been set + // so it shows at startup + } else { + userData.removeObject(forKey: dontShowGetStartedKey) + userData.set(false, forKey: shouldShowGetStartedKey) + // remove the old, confusing value and set the new value + return false + } + } + return userData.bool(forKey: shouldShowGetStartedKey) } } diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index 3bde31037a..6b5f476c2a 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -18,7 +18,8 @@ private let checkedProfilesKey = "CheckedProfiles" // External strings let userTextKey = "UserText" let userTextSizeKey = "UserTextSize" -let dontShowGetStartedKey = "DontShowGetStarted" +let dontShowGetStartedKey = "DontShowGetStarted" // older preference setting name, use shouldShowGetStartedKey +let shouldShowGetStartedKey = "ShouldShowGetStarted" let launchedFromUrlNotification = NSNotification.Name("LaunchedFromUrlNotification") let urlKey = "url" @@ -269,7 +270,14 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg orientation: orientation) actionButton.title = "Share" - dropdownItems = [textSizeButton, trashButton, getStartedButton, infoButton] + let settingsButton = createNavBarButton(with: #imageLiteral(resourceName: "more.png"), // should find a gear image and use that instead of re-using 'more' + highlightedImage: #imageLiteral(resourceName: "more-selected.png"), + imageScale: imageScaleF, + action: #selector(self.settingsButtonClick), + orientation: orientation) + settingsButton.title = "Settings" + + dropdownItems = [textSizeButton, trashButton, infoButton, getStartedButton, settingsButton] var scaleF: CGFloat = (UIScreen.main.scale == 2.0) ? 2.0 : 1.5 scaleF *= (UIDevice.current.userInterfaceIdiom == .phone) ? 1.0 : 2.0 @@ -535,6 +543,13 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg } } + @objc func settingsButtonClick(_ sender: Any) { + _ = dismissDropDownMenu() + popover?.dismiss(animated: false) + + Manager.shared.manageLanguages(inVC: self) + } + @objc func actionButtonClick(_ sender: Any) { _ = dismissDropDownMenu() popover?.dismiss(animated: false) @@ -981,6 +996,9 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg if userData.bool(forKey: dontShowGetStartedKey) { return false } + if !userData.bool(forKey: shouldShowGetStartedKey) { + return false + } if !AppDelegate.isKeymanEnabledSystemWide() { return true From 6fb5207df7cc6633d8f76a78d9052cde78cc2044 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 9 May 2019 22:10:01 -0500 Subject: [PATCH 02/47] save user defaults when changed and set the old preference when toggled --- .../GetStartedViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift index 9c6033570f..0908aa27fe 100644 --- a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift +++ b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift @@ -179,15 +179,15 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi @objc func switchValueChanged(_ sender: Any) { let userData = AppDelegate.activeUserDefaults() if let toggle = sender as? UISwitch { - userData.set(toggle.isOn, forKey: shouldShowGetStartedKey) + userData.set(toggle.isOn, forKey: dontShowGetStartedKey) userData.synchronize() } } - + private var dontShowGetStarted: Bool { return !shouldShowGetStarted } - + private var shouldShowGetStarted: Bool { let userData = AppDelegate.activeUserDefaults() var value: Any? = userData.object(forKey: shouldShowGetStartedKey) @@ -195,11 +195,13 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi value = userData.object(forKey: dontShowGetStartedKey) if nil == value { userData.set(true, forKey: shouldShowGetStartedKey) + userData.synchronize() return true // default to showing when no preference has been set // so it shows at startup } else { userData.removeObject(forKey: dontShowGetStartedKey) userData.set(false, forKey: shouldShowGetStartedKey) + userData.synchronize() // remove the old, confusing value and set the new value return false } From 2c63fbb285a6a195b790e9a7da8954a4c6cf22bb Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Tue, 14 May 2019 19:04:03 -0500 Subject: [PATCH 03/47] settings starts to show up though you cannot exit settings yet! --- common/predictive-text/package-lock.json | 41 ++--- .../KeymanEngine.xcodeproj/project.pbxproj | 16 ++ .../KMEI/KeymanEngine/Classes/Manager.swift | 9 +- .../Classes/Settings/Settings.storyboard | 148 ++++++++++++++++++ .../Settings/SettingsViewController.swift | 119 ++++++++++++++ .../Keyman/Keyman/MainViewController.swift | 2 +- web/package-lock.json | 41 ++--- 7 files changed, 310 insertions(+), 66 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 4c369f1ec9..49a9963a70 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1177,8 +1177,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1199,14 +1198,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1221,20 +1218,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1351,8 +1345,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1364,7 +1357,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1379,7 +1371,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1387,14 +1378,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1413,7 +1402,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1494,8 +1482,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1507,7 +1494,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1593,8 +1579,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1630,7 +1615,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1650,7 +1634,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1694,14 +1677,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 080d66f3c0..29a3d5b097 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -100,6 +100,8 @@ 9A31E2D0224AE8BF00D9A491 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; 9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; }; + 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A60763C22892485003BCFBA /* Settings.storyboard */; }; + 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A9CB084224170F700231FB9 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; @@ -289,6 +291,8 @@ 9A082558227589360051EBB0 /* Formatter+ISODateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Formatter+ISODateExtension.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 = ""; }; 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelPickerViewController.swift; sourceTree = ""; }; 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APILexicalModelRepository.swift; sourceTree = ""; }; 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepository.swift; sourceTree = ""; }; @@ -564,6 +568,15 @@ path = KeymanEngineTests; sourceTree = ""; }; + 9A60763E22892495003BCFBA /* Settings */ = { + isa = PBXGroup; + children = ( + 9A60763C22892485003BCFBA /* Settings.storyboard */, + 9A60764322893A4E003BCFBA /* SettingsViewController.swift */, + ); + name = Settings; + sourceTree = ""; + }; C024C9921FA6EB340060583B /* Notification */ = { isa = PBXGroup; children = ( @@ -739,6 +752,7 @@ C024C9921FA6EB340060583B /* Notification */, C07A9D881FD1760800828ADD /* KeyboardRepository */, C0A93A531F8B21240079948B /* Manager.swift */, + 9A60763E22892495003BCFBA /* Settings */, CE25CCBB1F1DA72A005AA2BC /* HTTPRequest */, 987F00041AB8FCB700998116 /* KeyboardMenuView */, 980CC8E119D51EF00089BB57 /* URLProtocol */, @@ -955,6 +969,7 @@ buildActionMask = 2147483647; files = ( C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */, + 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1153,6 +1168,7 @@ C024C9941FA6EB470060583B /* NotificationName.swift in Sources */, C0C16A891FA8146300F090BA /* KeymanWebViewController.swift in Sources */, 9A079DCA222E050E00581263 /* LexicalModelKeymanPackage.swift in Sources */, + 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */, C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */, C07A9D8E1FD1798900828ADD /* APIKeyboardRepository.swift in Sources */, C0959CD41F99C44E00B616BC /* Constants.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 7c9818fb79..9f64c8cdb3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -215,9 +215,8 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management - public func manageLanguages(inVC: UIViewController) -> Void { -// let languagesVC = LanguageViewController(Manager.shared.apiKeyboardRepository) //later Languages -// let rep = Manager.shared.apiKeyboardRepository + public func manageKeymanEngine(inVC: UIViewController) -> Void { + let settingsVC = SettingsViewController(style: UITableViewStyle.grouped) // let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) // navigationController?.pushViewController(languagesVC, animated: true) @@ -225,7 +224,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // if UIDevice.current.userInterfaceIdiom == .phone { // present(languagesVC, animated: true, completion: nil) // } else { - let alertController = UIAlertController(title: "Languages Settings", message: "Languages installed", + let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", preferredStyle: UIAlertControllerStyle.alert) alertController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, @@ -234,7 +233,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat style: UIAlertActionStyle.default, handler: nil)) - inVC.present(alertController, animated: true, completion: nil) + inVC.present(settingsVC, animated: true, completion: nil) // let popover = UIPopoverController(contentViewController: languagesVC) // popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard new file mode 100644 index 0000000000..1a2e72baeb --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift new file mode 100644 index 0000000000..08e00985cf --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -0,0 +1,119 @@ +// +// SettingsViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 5/13/19. +// Copyright © 2019 SIL International. All rights reserved. +// + +import UIKit + +open class SettingsViewController: UITableViewController { + + override open func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override open func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 1 + } + + override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 3 + } + + override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + var reuseID: String = "foo" + let lastIndex = indexPath.index(before: indexPath.endIndex) + let rowType = indexPath[lastIndex] + switch(rowType) { + case 0: + reuseID = "languages" + case 1: + reuseID = "showbanner" + case 2: + reuseID = "showgetstarted" + default: + log.error("undefined indexPath(\rowType") + reuseID = "foo" + } + if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { + return cell + } + + let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) + + // Configure the cell... + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) + cell.selectedBackgroundView = selectionColor + switch(rowType) { + case 0: + cell.textLabel?.text = "Installed Languages" + cell.accessoryType = .disclosureIndicator + case 1: + cell.textLabel?.text = "Show Banner" + case 2: + cell.textLabel?.text = "Show 'Get Started' at startup'" + default: + log.error("undefined indexPath(\rowType") + } + + return cell + } + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override open func prepare(for segue: UIStoryboardSegue, sender: Any?) { + log.info("prepare for segue") + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + +} diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index 6b5f476c2a..f070b6b6fd 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -547,7 +547,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg _ = dismissDropDownMenu() popover?.dismiss(animated: false) - Manager.shared.manageLanguages(inVC: self) + Manager.shared.manageKeymanEngine(inVC: self) } @objc func actionButtonClick(_ sender: Any) { diff --git a/web/package-lock.json b/web/package-lock.json index 70240f8bf1..d268683e86 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1274,8 +1274,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1296,14 +1295,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1318,20 +1315,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1448,8 +1442,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1461,7 +1454,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1476,7 +1468,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1484,14 +1475,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1510,7 +1499,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1591,8 +1579,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1604,7 +1591,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1690,8 +1676,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1727,7 +1712,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1747,7 +1731,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1791,14 +1774,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, From 29344b739c3ba57af547adc0f6c7fee893be20a4 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 20 May 2019 00:57:00 -0500 Subject: [PATCH 04/47] launch SettingsViewController without Navigator, can't back out yet --- .../KeymanEngine.xcodeproj/project.pbxproj | 32 +++- .../KeyboardNameTableViewCell.swift | 2 + .../LexicalModelInfoViewController.swift | 2 +- .../KMEI/KeymanEngine/Classes/Manager.swift | 33 +++- .../Classes/Settings/Settings.storyboard | 53 +----- .../Settings/SettingsViewController.swift | 165 ++++++++++++++---- .../SetUpViewController.xib | 15 +- 7 files changed, 203 insertions(+), 99 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 29a3d5b097..4ee9bd6a0d 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -98,9 +98,23 @@ 9A31E2CE224AE8B100D9A491 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; 9A31E2CF224AE8B800D9A491 /* LexicalModelRepositoryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */; }; 9A31E2D0224AE8BF00D9A491 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; + 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62161F67CFB800268D03 /* KeyboardPickerViewController.swift */; }; + 9A3B14BB228F592F0052A11F /* LanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04C2A6C1F6B7D9A00BA42B6 /* LanguageViewController.swift */; }; + 9A3B14BC228F59380052A11F /* LanguageDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */; }; + 9A3B14BD228F593C0052A11F /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; + 9A3B14BE228F59420052A11F /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; + 9A3B14BF228F59450052A11F /* KeyboardInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62141F67C8D500268D03 /* KeyboardInfoViewController.swift */; }; + 9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; + 9A3B14C1228F595A0052A11F /* KeyboardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3A02098993900040A69 /* KeyboardError.swift */; }; + 9A3B14C3228F59C10052A11F /* KeyboardPickerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C620F1F67BD4500268D03 /* KeyboardPickerButton.swift */; }; + 9A3B14C4228F59C70052A11F /* KeyboardPickerBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C620B1F6792A200268D03 /* KeyboardPickerBarButtonItem.swift */; }; + 9A3B14C6228F59E00052A11F /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C040E50F1F8606E300901EE4 /* TextField.swift */; }; + 9A3B14C7228F59E40052A11F /* TextFieldDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8C1F87480700AF3785 /* TextFieldDelegateProxy.swift */; }; + 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8E1F8750B700AF3785 /* TextView.swift */; }; + 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B901F8763E100AF3785 /* TextViewDelegateProxy.swift */; }; + 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; }; 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; 9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; }; - 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A60763C22892485003BCFBA /* Settings.storyboard */; }; 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; @@ -969,7 +983,6 @@ buildActionMask = 2147483647; files = ( C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */, - 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1045,6 +1058,10 @@ buildActionMask = 2147483647; files = ( 9A079E45223DBEF000581263 /* PopoverView.swift in Sources */, + 9A3B14C4228F59C70052A11F /* KeyboardPickerBarButtonItem.swift in Sources */, + 9A3B14BE228F59420052A11F /* LexicalModelInfoViewController.swift in Sources */, + 9A3B14C7228F59E40052A11F /* TextFieldDelegateProxy.swift in Sources */, + 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */, 9A31E2CC224AE89F00D9A491 /* NotificationCenter+Typed.swift in Sources */, 9A079E2C22361B4B00581263 /* KeyboardRepository.swift in Sources */, 9A31E2C8224AE87600D9A491 /* RegisteredFont.swift in Sources */, @@ -1060,16 +1077,21 @@ 9A079E46223DBEF900581263 /* KeyPreviewView.swift in Sources */, 9A079DE32231A16600581263 /* Constants.swift in Sources */, 9A31E2C1224AAD8300D9A491 /* FontManager.swift in Sources */, + 9A3B14C6228F59E00052A11F /* TextField.swift in Sources */, + 9A3B14C3228F59C10052A11F /* KeyboardPickerButton.swift in Sources */, 9A079E29223619F100581263 /* InputViewController.swift in Sources */, 9A079E28223619D300581263 /* Options.swift in Sources */, 9A079DE42231A1ED00581263 /* Log.swift in Sources */, 9A31E2C5224AE83D00D9A491 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift in Sources */, 9A079DDB2231A0F100581263 /* KMPKeyboard.swift in Sources */, + 9A3B14BD228F593C0052A11F /* LexicalModelPickerViewController.swift in Sources */, 9A079DDF2231A13A00581263 /* Keyboard.swift in Sources */, 9A31E2C2224AAD9A00D9A491 /* Notifications.swift in Sources */, 9A079DDD2231A11C00581263 /* InstallableKeyboard.swift in Sources */, 9A079DD2223194B100581263 /* KeymanEngineTests.swift in Sources */, 9A079E41223B602B00581263 /* LexicalModel.swift in Sources */, + 9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */, + 9A3B14BB228F592F0052A11F /* LanguageViewController.swift in Sources */, 9A31E2CE224AE8B100D9A491 /* LexicalModelRepository.swift in Sources */, 9A079E2E22361B8100581263 /* KeymanResponder+Types.swift in Sources */, 9A079DE12231A14D00581263 /* Storage.swift in Sources */, @@ -1079,17 +1101,23 @@ 9A31E2CB224AE89600D9A491 /* LexicalModelKeymanPackage.swift in Sources */, 9A31E2CF224AE8B800D9A491 /* LexicalModelRepositoryDelegate.swift in Sources */, 9A31E2C9224AE88600D9A491 /* LanguagesAPICall.swift in Sources */, + 9A3B14C1228F595A0052A11F /* KeyboardError.swift in Sources */, 9A079E2B22361A3200581263 /* KeymanWebViewController.swift in Sources */, 9A31E2CD224AE8A700D9A491 /* NotificationObserver.swift in Sources */, 9A079E382238680700581263 /* KMPLexicalModel.swift in Sources */, + 9A3B14BC228F59380052A11F /* LanguageDetailViewController.swift in Sources */, 9A079E2F22361BA100581263 /* Reachability.m in Sources */, 9A31E2C4224AB2A300D9A491 /* UIButton+Helpers.swift in Sources */, 9A079DDE2231A13100581263 /* FullKeyboardID.swift in Sources */, + 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */, 9A31E2CA224AE88E00D9A491 /* LexicalModelAPICall.swift in Sources */, 9A079E3222361C3B00581263 /* Migrations.swift in Sources */, 9A08255A227589360051EBB0 /* Formatter+ISODateExtension.swift in Sources */, + 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */, 9A079E2A223619FC00581263 /* KeymanWebDelegate.swift in Sources */, + 9A3B14BF228F59450052A11F /* KeyboardInfoViewController.swift in Sources */, 9A31E2C3224AADBB00D9A491 /* NotificationName.swift in Sources */, + 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */, 9A079DDA2231A0B900581263 /* KeymanPackage.swift in Sources */, 9A079DE22231A15900581263 /* Manager.swift in Sources */, 9A079E47223DBF0100581263 /* SubKeysView.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift index a5455043c4..5b71148716 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift @@ -6,6 +6,8 @@ // Copyright © 2017 SIL International. All rights reserved. // +import UIKit + private let cellSelectedTag = 4576 // arbitrary number class KeyboardNameTableViewCell: UITableViewCell, UIAlertViewDelegate { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift index c97233f2d1..c6109e02e5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift @@ -5,7 +5,7 @@ // Created by Randy Boring on 3/19/19. // Copyright © 2019 SIL International. All rights reserved. // - +import UIKit import Foundation class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 9f64c8cdb3..141d54c9d1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -216,7 +216,21 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management public func manageKeymanEngine(inVC: UIViewController) -> Void { - let settingsVC = SettingsViewController(style: UITableViewStyle.grouped) + +// for b in Bundle.allBundles { +// do { +// let sb = try UIStoryboard(name: "Settings", bundle: nil) +// if let vc = sb.instantiateInitialViewController() { +// inVC.present(vc, animated: true, completion: { +// log.info("presented settings") +// }) +// } +// } catch { +// log.info("bundle \(b) does not contain Settings.storyboard") +// } +// } + + let settingsVC = SettingsViewController() // let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) // navigationController?.pushViewController(languagesVC, animated: true) @@ -224,15 +238,16 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // if UIDevice.current.userInterfaceIdiom == .phone { // present(languagesVC, animated: true, completion: nil) // } else { - let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", - preferredStyle: UIAlertControllerStyle.alert) - alertController.addAction(UIAlertAction(title: "Cancel", - style: UIAlertActionStyle.cancel, - handler: nil)) - alertController.addAction(UIAlertAction(title: "OK", - style: UIAlertActionStyle.default, - handler: nil)) +// let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", +// preferredStyle: UIAlertControllerStyle.alert) +// alertController.addAction(UIAlertAction(title: "Cancel", +// style: UIAlertActionStyle.cancel, +// handler: nil)) +// alertController.addAction(UIAlertAction(title: "OK", +// style: UIAlertActionStyle.default, +// handler: nil)) +// inVC.present(settingsVC, animated: true, completion: nil) // let popover = UIPopoverController(contentViewController: languagesVC) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard index 1a2e72baeb..c5b79081f8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard @@ -6,29 +6,28 @@ - - - + + - + - - + @@ -106,43 +105,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 08e00985cf..ff253ac124 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -9,16 +9,64 @@ import UIKit open class SettingsViewController: UITableViewController { + private var itemsArray = [[String: String]]() + private var userLanguages: [String] = [String]() + override open func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + loadUserLanguages() + } + override open func viewDidLoad() { - super.viewDidLoad() + super.viewDidLoad() + + navigationController?.toolbar?.barTintColor = UIColor(red: 0.5, green: 0.75, + blue: 0.25, alpha: 0.9) - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + open func launchSettings(launchingVC: UIViewController, sender: Any?) -> Void { + let sb : UIStoryboard = UIStoryboard(name: "Settings", bundle: nil) + if let vc = sb.instantiateInitialViewController() { + launchingVC.present(vc, animated: true, completion: { + log.info("presented settings") + }) } + } +// +// convenience init() { +// self.init(style: UITableViewStyle.grouped) +// } + + init(/*storage: Storage*/) { +// self.storage = storage + super.init(nibName: nil, bundle: nil) + + itemsArray = [[String: String]]() + itemsArray.append([ + "title": "Installed Languages...", + "subtitle": "0", //count of installed languages as string + "reuseid" : "language" + ]) + + itemsArray.append([ + "title": "Show Banner", + "subtitle": "", + "reuseid" : "showbanner" + ]) + + itemsArray.append([ + "title": "Show 'Get Started' at startup", + "subtitle": "", + "reuseid" : "showgetstarted" + ]) + _ = view + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } // MARK: - Table view data source @@ -33,43 +81,80 @@ open class SettingsViewController: UITableViewController { } override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var reuseID: String = "foo" - let lastIndex = indexPath.index(before: indexPath.endIndex) - let rowType = indexPath[lastIndex] - switch(rowType) { - case 0: - reuseID = "languages" - case 1: - reuseID = "showbanner" - case 2: - reuseID = "showgetstarted" - default: - log.error("undefined indexPath(\rowType") - reuseID = "foo" - } - if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { + let cellIdentifier = itemsArray[indexPath.row]["reuseid"] + if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier!) { return cell } + let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) + cell.selectionStyle = .none - let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) - - // Configure the cell... - let selectionColor = UIView() - selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) - cell.selectedBackgroundView = selectionColor - switch(rowType) { - case 0: - cell.textLabel?.text = "Installed Languages" + switch(cellIdentifier) { + case "languages": cell.accessoryType = .disclosureIndicator - case 1: - cell.textLabel?.text = "Show Banner" - case 2: - cell.textLabel?.text = "Show 'Get Started' at startup'" + case "showbanner": + cell.accessoryType = .none + case "showgetstarted": + cell.accessoryType = .none default: - log.error("undefined indexPath(\rowType") + log.error("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")") + cell.accessoryType = .none + } + + return cell + } + + override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + cell.selectionStyle = .none + cell.accessoryType = .none + cell.textLabel?.text = itemsArray[indexPath.row]["title"] + cell.detailTextLabel?.text = itemsArray[indexPath.row]["subtitle"] + cell.tag = indexPath.row + + if indexPath.row == 0 { + cell.accessoryType = .disclosureIndicator + } else { + cell.isUserInteractionEnabled = false + cell.textLabel?.isEnabled = true + cell.detailTextLabel?.isEnabled = false } - return cell +// var reuseID: String = "foo" +// let lastIndex = indexPath.index(before: indexPath.endIndex) +// let rowType = indexPath[lastIndex] +// switch(rowType) { +// case 0: +// reuseID = "languages" +// case 1: +// reuseID = "showbanner" +// case 2: +// reuseID = "showgetstarted" +// default: +// log.error("undefined indexPath(\rowType") +// reuseID = "foo" +// } +// if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { +// return cell +// } +// +// let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) +// +// // Configure the cell... +// let selectionColor = UIView() +// selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) +// cell.selectedBackgroundView = selectionColor +// switch(rowType) { +// case 0: +// cell.textLabel?.text = "Installed Languages2" +// cell.accessoryType = .disclosureIndicator +// case 1: +// cell.textLabel?.text = "Show Banner" +// case 2: +// cell.textLabel?.text = "Show 'Get Started' at startup'" +// default: +// log.error("undefined indexPath(\rowType") +// } +// +// return cell } /* @@ -116,4 +201,12 @@ open class SettingsViewController: UITableViewController { // Pass the selected object to the new view controller. } + // MARK: - language access - + private func loadUserLanguages() { + //iterate the list of installed languages and save their names + userLanguages = ["en"] + + itemsArray[0]["subtitle"] = String(userLanguages.count) + } + } diff --git a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib index c849610801..a53d2a7972 100644 --- a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib +++ b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib @@ -1,7 +1,12 @@ - - + + + + + - + + + @@ -25,10 +30,10 @@ - + - + From 8514de228e54042f67d0bbaee14b78637e78ac3f Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 22 May 2019 21:54:15 -0500 Subject: [PATCH 05/47] put Done button at top of Keyman Settings in viewDidLoad --- .../KeymanEngine.xcodeproj/project.pbxproj | 6 + .../KMEI/KeymanEngine/Classes/Manager.swift | 46 +-- .../InstalledLanguagesViewController.swift | 376 ++++++++++++++++++ .../Settings/SettingsViewController.swift | 33 +- .../Keyman/Keyman/MainViewController.swift | 2 +- 5 files changed, 422 insertions(+), 41 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 4ee9bd6a0d..8e6b3ca3ce 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -113,6 +113,8 @@ 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8E1F8750B700AF3785 /* TextView.swift */; }; 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B901F8763E100AF3785 /* TextViewDelegateProxy.swift */; }; 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; }; + 9A3B14D2229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */; }; + 9A3B14D3229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.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 */; }; @@ -303,6 +305,7 @@ 9A079E3F223B602B00581263 /* LexicalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModel.swift; sourceTree = ""; }; 9A079E42223B61AE00581263 /* FullLexicalModelID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullLexicalModelID.swift; sourceTree = ""; }; 9A082558227589360051EBB0 /* Formatter+ISODateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Formatter+ISODateExtension.swift"; sourceTree = ""; }; + 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.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 = ""; }; @@ -587,6 +590,7 @@ children = ( 9A60763C22892485003BCFBA /* Settings.storyboard */, 9A60764322893A4E003BCFBA /* SettingsViewController.swift */, + 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */, ); name = Settings; sourceTree = ""; @@ -1112,6 +1116,7 @@ 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */, 9A31E2CA224AE88E00D9A491 /* LexicalModelAPICall.swift in Sources */, 9A079E3222361C3B00581263 /* Migrations.swift in Sources */, + 9A3B14D3229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */, 9A08255A227589360051EBB0 /* Formatter+ISODateExtension.swift in Sources */, 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */, 9A079E2A223619FC00581263 /* KeymanWebDelegate.swift in Sources */, @@ -1178,6 +1183,7 @@ C06D37431F81F5C400F61AE0 /* KeyboardPickerBarButtonItem.swift in Sources */, C082CE151F90AFD400860F02 /* Collection+SafeAccess.swift in Sources */, C06D37441F81F5C400F61AE0 /* KeyboardNameTableViewCell.swift in Sources */, + 9A3B14D2229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */, C07A9D8A1FD1762700828ADD /* KeyboardRepository.swift in Sources */, C08E69911FDA6F6F0026056B /* FullKeyboardID.swift in Sources */, 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 141d54c9d1..9105506571 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -214,46 +214,14 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat } // MARK: - Keyboard management - - public func manageKeymanEngine(inVC: UIViewController) -> Void { - -// for b in Bundle.allBundles { -// do { -// let sb = try UIStoryboard(name: "Settings", bundle: nil) -// if let vc = sb.instantiateInitialViewController() { -// inVC.present(vc, animated: true, completion: { -// log.info("presented settings") -// }) -// } -// } catch { -// log.info("bundle \(b) does not contain Settings.storyboard") -// } -// } - + + public func showKeymanEngineSettings(inVC: UIViewController) -> Void { + hideKeyboard() let settingsVC = SettingsViewController() -// let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) -// navigationController?.pushViewController(languagesVC, animated: true) - -// textView.isUserInteractionEnabled = false -// if UIDevice.current.userInterfaceIdiom == .phone { -// present(languagesVC, animated: true, completion: nil) -// } else { - -// let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", -// preferredStyle: UIAlertControllerStyle.alert) -// alertController.addAction(UIAlertAction(title: "Cancel", -// style: UIAlertActionStyle.cancel, -// handler: nil)) -// alertController.addAction(UIAlertAction(title: "OK", -// style: UIAlertActionStyle.default, -// handler: nil)) -// - inVC.present(settingsVC, animated: true, completion: nil) - -// let popover = UIPopoverController(contentViewController: languagesVC) -// popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) -// (from: (navigationItem.rightBarButtonItems?[8])!, permittedArrowDirections: .any, animated: true) -// } + let nc = UINavigationController(rootViewController: settingsVC) + nc.modalTransitionStyle = .coverVertical + nc.modalPresentationStyle = .pageSheet + inVC.present(nc, animated: true) } /// Sets the current keyboard, querying from the user's list of keyboards. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift new file mode 100644 index 0000000000..69c640ce11 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -0,0 +1,376 @@ +// +// InstalledLanguagesViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 5/20/19. +// Copyright © 2019 SIL International. All rights reserved. +// +// +import QuartzCore +import UIKit + +private let activityViewTag = -2 +private let toolbarButtonTag = 100 +private let toolbarLabelTag = 101 +private let toolbarActivityIndicatorTag = 102 + +class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelegate { + private var userKeyboards: [String: InstallableKeyboard] = [:] + private var userLexicalModels: [String: InstallableLexicalModel] = [:] + private var sectionIndexTitles: [String] = [] + private var indices: [Int] = [] + private var selectedSection = 0 + private var isUpdate = false + private var languages: [Language] = [] + private let keyboardRepository: KeyboardRepository? + private let lexicalModelRepository: LexicalModelRepository? + + private var keyboardDownloadStartedObserver: NotificationObserver? + private var keyboardDownloadFailedObserver: NotificationObserver? + + init(_ keyboardRepository: KeyboardRepository) { + self.keyboardRepository = keyboardRepository + self.lexicalModelRepository = nil + super.init(nibName: nil, bundle: nil) + keyboardRepository.delegate = self + } + + init(_ lexicalModelRepository: LexicalModelRepository) { + self.lexicalModelRepository = lexicalModelRepository + self.keyboardRepository = nil + super.init(nibName: nil, bundle: nil) + lexicalModelRepository.delegate = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + super.loadView() + if let languageDict = keyboardRepository?.languages { + languages = languageList(languageDict) + } else { + keyboardRepository?.fetch() + } + + loadUserKeyboards() + } + + override func viewDidLoad() { + super.viewDidLoad() + title = "Add New Keyboard" + selectedSection = NSNotFound + keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( + forName: Notifications.keyboardDownloadStarted, + observer: self, + function: InstalledLanguagesViewController.keyboardDownloadStarted) + keyboardDownloadFailedObserver = NotificationCenter.default.addObserver( + forName: Notifications.keyboardDownloadFailed, + observer: self, + function: InstalledLanguagesViewController.keyboardDownloadFailed) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.setToolbarHidden(true, animated: true) + // if no rows to show yet, show a loading indicator + if numberOfSections(in: tableView) == 0 { + showActivityView() + } + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return languages.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellIdentifierType1 = "CellType1" + let cellIdentifierType2 = "CellType2" + let keyboards = languages[indexPath.section].keyboards! + let cellIdentifier = (keyboards.count < 2) ? cellIdentifierType1 : cellIdentifierType2 + let cell: UITableViewCell + if let reusedCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { + cell = reusedCell + } else { + if keyboards.count < 2 { + 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 + } else { + cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier) + cell.accessoryType = .disclosureIndicator + 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 + } + } + cell.detailTextLabel?.text = keyboards.count < 2 ? (keyboards.first?.name ?? "") : "" + return cell + } + + // Create sections by first letter + override func sectionIndexTitles(for tableView: UITableView) -> [String] { + if !sectionIndexTitles.isEmpty { + return sectionIndexTitles + } + + sectionIndexTitles = [] + indices = [] + for (index, language) in languages.enumerated() { + let firstLetter = String(language.name.prefix(1)).uppercased() + if !sectionIndexTitles.contains(firstLetter) { + sectionIndexTitles.append(firstLetter) + indices.append(index) + } + } + return sectionIndexTitles + } + + override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + return indices[index] + } + + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + guard let language = languages[safe: indexPath.section] else { + return + } + + cell.textLabel?.text = language.name + + if cell.accessoryType == .disclosureIndicator { + return + } + guard let cell = cell as? KeyboardNameTableViewCell else { + return + } + + let languageID = language.id + let keyboardID = language.keyboards![0].id + + if isAdded(languageID: languageID, keyboardID: keyboardID) { + 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.stateForKeyboard(withID: keyboardID) + cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + selectedSection = indexPath.section + tableView.cellForRow(at: indexPath)?.isSelected = false + if tableView.cellForRow(at: indexPath)?.accessoryType == .disclosureIndicator { + let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" + showLanguageDetailView(title: title, languageIndex: indexPath.section) + return + } + + let language = languages[indexPath.section] + let keyboardIndex = 0; + let keyboard = language.keyboards![keyboardIndex] + + let state = Manager.shared.stateForKeyboard(withID: keyboard.id) + if state != .downloading { + isUpdate = state != .needsDownload + let alertController = UIAlertController(title: "\(language.name): \(keyboard.name)", + message: "Would you like to download this keyboard?", + 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(keyboardIndex)} )) + self.present(alertController, animated: true, completion: nil) + } + } + + override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { + let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" + showLanguageDetailView(title: title, languageIndex: indexPath.section) + } + + private func showLanguageDetailView(title: String, languageIndex: Int) { + let langDetailView = LanguageDetailViewController(language: languages[languageIndex]) + langDetailView.title = title + navigationController?.pushViewController(langDetailView, animated: true) + } + + func errorAcknowledgmentHandler(withAction action: UIAlertAction) { + if !languages.isEmpty { + navigationController?.popToRootViewController(animated: true) + } + } + + func downloadHandler(_ keyboardIndex: Int) { + let language = languages[selectedSection] + let keyboard = language.keyboards![keyboardIndex] + Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate) + } + + private func keyboardDownloadStarted() { + view.isUserInteractionEnabled = false + navigationItem.setHidesBackButton(true, animated: true) + + guard let toolbar = navigationController?.toolbar else { + return + } + + let labelFrame = CGRect(origin: toolbar.frame.origin, + size: CGSize(width: toolbar.frame.width * 0.95, + height: toolbar.frame.height * 0.7)) + let label = UILabel(frame: labelFrame) + label.backgroundColor = UIColor.clear + label.textColor = UIColor.white + label.textAlignment = .center + label.center = CGPoint(x: toolbar.frame.width * 0.5, y: toolbar.frame.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: toolbar.frame.width - indicatorView.frame.width, + y: toolbar.frame.height * 0.5) + indicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin] + indicatorView.tag = toolbarActivityIndicatorTag + indicatorView.startAnimating() + toolbar.viewWithTag(toolbarButtonTag)?.removeFromSuperview() + toolbar.viewWithTag(toolbarLabelTag)?.removeFromSuperview() + toolbar.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() + toolbar.addSubview(label) + toolbar.addSubview(indicatorView) + navigationController?.setToolbarHidden(false, animated: true) + } + + private func keyboardDownloadFailed() { + view.isUserInteractionEnabled = true + navigationItem.setHidesBackButton(false, animated: true) + } + + func showActivityView() { + view.isUserInteractionEnabled = false + let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) + let activityView = UIView(frame: indicatorView.bounds.insetBy(dx: -10.0, dy: -10.0)) + activityView.backgroundColor = UIColor(white: 0.5, alpha: 0.8) + activityView.layer.cornerRadius = 6.0 + activityView.center = view.center + activityView.tag = activityViewTag + activityView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, + .flexibleBottomMargin] + + indicatorView.center = CGPoint(x: activityView.bounds.size.width * 0.5, y: activityView.bounds.size.height * 0.5) + indicatorView.startAnimating() + activityView.addSubview(indicatorView) + view.addSubview(activityView) + } + + func dismissActivityView() { + let activityView = view.viewWithTag(activityViewTag) + activityView?.removeFromSuperview() + view.isUserInteractionEnabled = true + } + + func loadUserKeyboards() { + userKeyboards = [:] + guard let userKbList = Storage.active.userDefaults.userKeyboards else { + return + } + + for kb in userKbList { + let dictKey = "\(kb.languageID)_\(kb.id)" + userKeyboards[dictKey] = kb + } + } + + func loadUserLexicalModels() { + userLexicalModels = [:] + guard let userLmList = Storage.active.userDefaults.userLexicalModels else { + return + } + + for lm in userLmList { + let dictKey = "\(lm.languageID)_\(lm.id)" + userLexicalModels[dictKey] = lm + } + } + + private func isAdded(languageID: String?, keyboardID: String?) -> Bool { + guard let languageID = languageID, let keyboardID = keyboardID else { + return false + } + return userKeyboards["\(languageID)_\(keyboardID)"] != nil + } + + private func showConnectionErrorAlert() { + dismissActivityView() + let alertController = UIAlertController(title: "Connection Error", + message: "Could not reach Keyman server. Please try again later.", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.default, + handler: errorAcknowledgmentHandler)) + + self.present(alertController, animated: true, completion: nil) + } +} + +// MARK: - KeyboardRepositoryDelegate +extension InstalledLanguagesViewController: KeyboardRepositoryDelegate { + func keyboardRepositoryDidFetch(_ repository: KeyboardRepository) { + if let languageDict = repository.languages { + languages = languageList(languageDict) + } + self.dismissActivityView() + self.tableView.reloadData() + if self.numberOfSections(in: self.tableView) == 0 { + self.showConnectionErrorAlert() + } + } + + func keyboardRepository(_ repository: KeyboardRepository, didFailFetch error: Error) { + dismissActivityView() + showConnectionErrorAlert() + } + + private func languageList(_ languageDict: [String: Language]) -> [Language] { + return languageDict.values.sorted { a, b -> Bool in + a.name.localizedCaseInsensitiveCompare(b.name) == .orderedAscending + } + } +} + +// MARK: - LexicalModelRepositoryDelegate +//may not need this, as we don't plan ever to fetch the whole lexical model repository (or even the list of all available) +extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { + func lexicalModelRepositoryDidFetch(_ repository: LexicalModelRepository) { + if let languageDict = repository.languages { + languages = languageList(languageDict) + } + self.dismissActivityView() + self.tableView.reloadData() + if self.numberOfSections(in: self.tableView) == 0 { + self.showConnectionErrorAlert() + } + } + + func lexicalModelRepository(_ repository: LexicalModelRepository, didFailFetch error: Error) { + dismissActivityView() + showConnectionErrorAlert() + } +} + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index ff253ac124..d7281ddaa1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -21,9 +21,17 @@ open class SettingsViewController: UITableViewController { override open func viewDidLoad() { super.viewDidLoad() + title = "Keyman Settings" + let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + action: #selector(self.doneClicked)) + navigationItem.leftBarButtonItem = doneButton + navigationController?.toolbar?.barTintColor = UIColor(red: 0.5, green: 0.75, blue: 0.25, alpha: 0.9) - + } + + @objc func doneClicked(_ sender: Any) { + Manager.shared.dismissKeyboardPicker(self) } open func launchSettings(launchingVC: UIViewController, sender: Any?) -> Void { @@ -209,4 +217,27 @@ open class SettingsViewController: UITableViewController { itemsArray[0]["subtitle"] = String(userLanguages.count) } + public func setIsDoneButtonEnabled(_ nc: UINavigationController, _ value: Bool) { + let doneOrCancel = value + if doneOrCancel { + let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + action: nil /* #selector(self.doneClicked) */ ) + nc.navigationItem.leftBarButtonItem = doneButton + } else { + let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, + action: nil /* #selector(self.cancelClicked) */ ) + nc.navigationItem.leftBarButtonItem = cancelButton + } + } + + func showLanguages() { + let vc = InstalledLanguagesViewController(Manager.shared.apiKeyboardRepository) + if let nc = navigationController { + nc.pushViewController(vc, animated: true) + setIsDoneButtonEnabled(nc, true) + } else { + log.error("no navigation controller for showing languages???") + } + } + } diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index f070b6b6fd..17373dc30a 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -547,7 +547,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg _ = dismissDropDownMenu() popover?.dismiss(animated: false) - Manager.shared.manageKeymanEngine(inVC: self) + Manager.shared.showKeymanEngineSettings(inVC: self) } @objc func actionButtonClick(_ sender: Any) { From f364e6b79bd78b3635f1b772e8056548ef77d559 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Fri, 24 May 2019 00:30:59 -0500 Subject: [PATCH 06/47] clicking InstalledLanguages shows InstalledLanguagesViewController count the installed languages navigate back to Settings --- .../InstalledLanguagesViewController.swift | 24 +++------- .../Settings/SettingsViewController.swift | 47 +++++++++++++++++-- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 69c640ce11..30b9822433 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -22,24 +22,19 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega private var selectedSection = 0 private var isUpdate = false private var languages: [Language] = [] + private var installedLanguages: [String: Language] private let keyboardRepository: KeyboardRepository? private let lexicalModelRepository: LexicalModelRepository? private var keyboardDownloadStartedObserver: NotificationObserver? private var keyboardDownloadFailedObserver: NotificationObserver? - init(_ keyboardRepository: KeyboardRepository) { - self.keyboardRepository = keyboardRepository + init(_ givenLanguages: [String: Language]) { + self.installedLanguages = givenLanguages + self.keyboardRepository = nil self.lexicalModelRepository = nil super.init(nibName: nil, bundle: nil) - keyboardRepository.delegate = self - } - - init(_ lexicalModelRepository: LexicalModelRepository) { - self.lexicalModelRepository = lexicalModelRepository - self.keyboardRepository = nil - super.init(nibName: nil, bundle: nil) - lexicalModelRepository.delegate = self +// keyboardRepository.delegate = self } required init?(coder aDecoder: NSCoder) { @@ -48,18 +43,13 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega override func loadView() { super.loadView() - if let languageDict = keyboardRepository?.languages { - languages = languageList(languageDict) - } else { - keyboardRepository?.fetch() - } - + languages = languageList(installedLanguages) loadUserKeyboards() } override func viewDidLoad() { super.viewDidLoad() - title = "Add New Keyboard" + title = "Installed Languages" selectedSection = NSNotFound keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( forName: Notifications.keyboardDownloadStarted, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index d7281ddaa1..5aa92b59ff 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -10,7 +10,7 @@ import UIKit open class SettingsViewController: UITableViewController { private var itemsArray = [[String: String]]() - private var userLanguages: [String] = [String]() + private var userLanguages: [String: Language] = [:] override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -163,6 +163,25 @@ open class SettingsViewController: UITableViewController { // } // // return cell + } + + // In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath: + override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.cellForRow(at: indexPath)?.isSelected = false + performAction(for: indexPath) + } + + override open func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { + performAction(for: indexPath) + } + + private func performAction(for indexPath: IndexPath) { + switch indexPath.section { + case 0: + showLanguages() + default: + break + } } /* @@ -212,8 +231,28 @@ open class SettingsViewController: UITableViewController { // MARK: - language access - private func loadUserLanguages() { //iterate the list of installed languages and save their names - userLanguages = ["en"] - + // Get keyboards list if it exists in user defaults, otherwise create a new one + let userDefaults = Storage.active.userDefaults + let userKeyboards = userDefaults.userKeyboards ?? [] + + var keyboardLanguages = [String: Language]() + for k in userKeyboards { + let l = k.languageID + keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)], lexicalModels: nil, font: nil, oskFont: nil) + } + // there shouldn't be any lexical models for languages that don't have a keyboard installed + // but check + let userLexicalModels = userDefaults.userLexicalModels ?? [] + for lm in userLexicalModels { + let l = lm.languageID + if let langName = keyboardLanguages[l]?.name { + log.info("keyboard language \(l) \(langName) has lexical model") + } else { + log.error("lexical model language \(l) has no keyboard installed!") + } + } + + userLanguages = keyboardLanguages itemsArray[0]["subtitle"] = String(userLanguages.count) } @@ -231,7 +270,7 @@ open class SettingsViewController: UITableViewController { } func showLanguages() { - let vc = InstalledLanguagesViewController(Manager.shared.apiKeyboardRepository) + let vc = InstalledLanguagesViewController(userLanguages) if let nc = navigationController { nc.pushViewController(vc, animated: true) setIsDoneButtonEnabled(nc, true) From 2943130ca50e43865a5022e97ec880d7688f1618 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 9 May 2019 19:03:54 -0500 Subject: [PATCH 07/47] start of UI for lexical models begin with top menu, stub for Settings, and changing the underlying key for showing Get Started --- .../KMEI/KeymanEngine/Classes/Manager.swift | 27 +++++++++++++++++++ .../GetStartedViewController.swift | 24 ++++++++++++++--- .../Keyman/Keyman/MainViewController.swift | 22 +++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 2f518adf95..3cf79c2ede 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -215,6 +215,33 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management + public func manageLanguages(inVC: UIViewController) -> Void { +// let languagesVC = LanguageViewController(Manager.shared.apiKeyboardRepository) //later Languages +// let rep = Manager.shared.apiKeyboardRepository +// let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) +// navigationController?.pushViewController(languagesVC, animated: true) + +// textView.isUserInteractionEnabled = false +// if UIDevice.current.userInterfaceIdiom == .phone { +// present(languagesVC, animated: true, completion: nil) +// } else { + let alertController = UIAlertController(title: "Languages Settings", message: "Languages installed", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "Cancel", + style: UIAlertActionStyle.cancel, + handler: nil)) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.default, + handler: nil)) + + inVC.present(alertController, animated: true, completion: nil) + +// let popover = UIPopoverController(contentViewController: languagesVC) +// popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) +// (from: (navigationItem.rightBarButtonItems?[8])!, permittedArrowDirections: .any, animated: true) +// } + } + /// Sets the current keyboard, querying from the user's list of keyboards. /// /// - Precondition: diff --git a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift index 24cc430fea..9c6033570f 100644 --- a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift +++ b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift @@ -179,13 +179,31 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi @objc func switchValueChanged(_ sender: Any) { let userData = AppDelegate.activeUserDefaults() if let toggle = sender as? UISwitch { - userData.set(toggle.isOn, forKey: dontShowGetStartedKey) + userData.set(toggle.isOn, forKey: shouldShowGetStartedKey) userData.synchronize() } } - + private var dontShowGetStarted: Bool { + return !shouldShowGetStarted + } + + private var shouldShowGetStarted: Bool { let userData = AppDelegate.activeUserDefaults() - return userData.bool(forKey: dontShowGetStartedKey) + var value: Any? = userData.object(forKey: shouldShowGetStartedKey) + if nil == value { // new key not present, try the old key + value = userData.object(forKey: dontShowGetStartedKey) + if nil == value { + userData.set(true, forKey: shouldShowGetStartedKey) + return true // default to showing when no preference has been set + // so it shows at startup + } else { + userData.removeObject(forKey: dontShowGetStartedKey) + userData.set(false, forKey: shouldShowGetStartedKey) + // remove the old, confusing value and set the new value + return false + } + } + return userData.bool(forKey: shouldShowGetStartedKey) } } diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index 3bde31037a..6b5f476c2a 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -18,7 +18,8 @@ private let checkedProfilesKey = "CheckedProfiles" // External strings let userTextKey = "UserText" let userTextSizeKey = "UserTextSize" -let dontShowGetStartedKey = "DontShowGetStarted" +let dontShowGetStartedKey = "DontShowGetStarted" // older preference setting name, use shouldShowGetStartedKey +let shouldShowGetStartedKey = "ShouldShowGetStarted" let launchedFromUrlNotification = NSNotification.Name("LaunchedFromUrlNotification") let urlKey = "url" @@ -269,7 +270,14 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg orientation: orientation) actionButton.title = "Share" - dropdownItems = [textSizeButton, trashButton, getStartedButton, infoButton] + let settingsButton = createNavBarButton(with: #imageLiteral(resourceName: "more.png"), // should find a gear image and use that instead of re-using 'more' + highlightedImage: #imageLiteral(resourceName: "more-selected.png"), + imageScale: imageScaleF, + action: #selector(self.settingsButtonClick), + orientation: orientation) + settingsButton.title = "Settings" + + dropdownItems = [textSizeButton, trashButton, infoButton, getStartedButton, settingsButton] var scaleF: CGFloat = (UIScreen.main.scale == 2.0) ? 2.0 : 1.5 scaleF *= (UIDevice.current.userInterfaceIdiom == .phone) ? 1.0 : 2.0 @@ -535,6 +543,13 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg } } + @objc func settingsButtonClick(_ sender: Any) { + _ = dismissDropDownMenu() + popover?.dismiss(animated: false) + + Manager.shared.manageLanguages(inVC: self) + } + @objc func actionButtonClick(_ sender: Any) { _ = dismissDropDownMenu() popover?.dismiss(animated: false) @@ -981,6 +996,9 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg if userData.bool(forKey: dontShowGetStartedKey) { return false } + if !userData.bool(forKey: shouldShowGetStartedKey) { + return false + } if !AppDelegate.isKeymanEnabledSystemWide() { return true From 4d0163a707b35b3c80ec7d7a7cdb5bd2b8492813 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 9 May 2019 22:10:01 -0500 Subject: [PATCH 08/47] save user defaults when changed and set the old preference when toggled --- .../GetStartedViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift index 9c6033570f..0908aa27fe 100644 --- a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift +++ b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift @@ -179,15 +179,15 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi @objc func switchValueChanged(_ sender: Any) { let userData = AppDelegate.activeUserDefaults() if let toggle = sender as? UISwitch { - userData.set(toggle.isOn, forKey: shouldShowGetStartedKey) + userData.set(toggle.isOn, forKey: dontShowGetStartedKey) userData.synchronize() } } - + private var dontShowGetStarted: Bool { return !shouldShowGetStarted } - + private var shouldShowGetStarted: Bool { let userData = AppDelegate.activeUserDefaults() var value: Any? = userData.object(forKey: shouldShowGetStartedKey) @@ -195,11 +195,13 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi value = userData.object(forKey: dontShowGetStartedKey) if nil == value { userData.set(true, forKey: shouldShowGetStartedKey) + userData.synchronize() return true // default to showing when no preference has been set // so it shows at startup } else { userData.removeObject(forKey: dontShowGetStartedKey) userData.set(false, forKey: shouldShowGetStartedKey) + userData.synchronize() // remove the old, confusing value and set the new value return false } From 79c0fa0c0687a399293cdb7e84de265626703f50 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Tue, 14 May 2019 19:04:03 -0500 Subject: [PATCH 09/47] settings starts to show up though you cannot exit settings yet! --- common/predictive-text/package-lock.json | 41 ++--- .../KeymanEngine.xcodeproj/project.pbxproj | 16 ++ .../KMEI/KeymanEngine/Classes/Manager.swift | 9 +- .../Classes/Settings/Settings.storyboard | 148 ++++++++++++++++++ .../Settings/SettingsViewController.swift | 119 ++++++++++++++ .../Keyman/Keyman/MainViewController.swift | 2 +- web/package-lock.json | 41 ++--- 7 files changed, 310 insertions(+), 66 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 4c369f1ec9..49a9963a70 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1177,8 +1177,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1199,14 +1198,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1221,20 +1218,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1351,8 +1345,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1364,7 +1357,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1379,7 +1371,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1387,14 +1378,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1413,7 +1402,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1494,8 +1482,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1507,7 +1494,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1593,8 +1579,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1630,7 +1615,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1650,7 +1634,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1694,14 +1677,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 7980c0b84c..ea0a624c7f 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -100,6 +100,8 @@ 9A31E2D0224AE8BF00D9A491 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; 9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; }; + 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A60763C22892485003BCFBA /* Settings.storyboard */; }; + 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A9CB084224170F700231FB9 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; @@ -290,6 +292,8 @@ 9A082558227589360051EBB0 /* Formatter+ISODateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Formatter+ISODateExtension.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 = ""; }; 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelPickerViewController.swift; sourceTree = ""; }; 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APILexicalModelRepository.swift; sourceTree = ""; }; 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepository.swift; sourceTree = ""; }; @@ -566,6 +570,15 @@ path = KeymanEngineTests; sourceTree = ""; }; + 9A60763E22892495003BCFBA /* Settings */ = { + isa = PBXGroup; + children = ( + 9A60763C22892485003BCFBA /* Settings.storyboard */, + 9A60764322893A4E003BCFBA /* SettingsViewController.swift */, + ); + name = Settings; + sourceTree = ""; + }; C024C9921FA6EB340060583B /* Notification */ = { isa = PBXGroup; children = ( @@ -750,6 +763,7 @@ C024C9921FA6EB340060583B /* Notification */, C07A9D881FD1760800828ADD /* KeyboardRepository */, C0A93A531F8B21240079948B /* Manager.swift */, + 9A60763E22892495003BCFBA /* Settings */, CE25CCBB1F1DA72A005AA2BC /* HTTPRequest */, 987F00041AB8FCB700998116 /* KeyboardMenuView */, 980CC8E119D51EF00089BB57 /* URLProtocol */, @@ -966,6 +980,7 @@ buildActionMask = 2147483647; files = ( C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */, + 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1165,6 +1180,7 @@ C024C9941FA6EB470060583B /* NotificationName.swift in Sources */, C0C16A891FA8146300F090BA /* KeymanWebViewController.swift in Sources */, 9A079DCA222E050E00581263 /* LexicalModelKeymanPackage.swift in Sources */, + 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */, C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */, C07A9D8E1FD1798900828ADD /* APIKeyboardRepository.swift in Sources */, C0959CD41F99C44E00B616BC /* Constants.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 3cf79c2ede..ce170f4d4d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -215,9 +215,8 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management - public func manageLanguages(inVC: UIViewController) -> Void { -// let languagesVC = LanguageViewController(Manager.shared.apiKeyboardRepository) //later Languages -// let rep = Manager.shared.apiKeyboardRepository + public func manageKeymanEngine(inVC: UIViewController) -> Void { + let settingsVC = SettingsViewController(style: UITableViewStyle.grouped) // let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) // navigationController?.pushViewController(languagesVC, animated: true) @@ -225,7 +224,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // if UIDevice.current.userInterfaceIdiom == .phone { // present(languagesVC, animated: true, completion: nil) // } else { - let alertController = UIAlertController(title: "Languages Settings", message: "Languages installed", + let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", preferredStyle: UIAlertControllerStyle.alert) alertController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, @@ -234,7 +233,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat style: UIAlertActionStyle.default, handler: nil)) - inVC.present(alertController, animated: true, completion: nil) + inVC.present(settingsVC, animated: true, completion: nil) // let popover = UIPopoverController(contentViewController: languagesVC) // popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard new file mode 100644 index 0000000000..1a2e72baeb --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift new file mode 100644 index 0000000000..08e00985cf --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -0,0 +1,119 @@ +// +// SettingsViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 5/13/19. +// Copyright © 2019 SIL International. All rights reserved. +// + +import UIKit + +open class SettingsViewController: UITableViewController { + + override open func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override open func numberOfSections(in tableView: UITableView) -> Int { + // #warning Incomplete implementation, return the number of sections + return 1 + } + + override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 3 + } + + override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + var reuseID: String = "foo" + let lastIndex = indexPath.index(before: indexPath.endIndex) + let rowType = indexPath[lastIndex] + switch(rowType) { + case 0: + reuseID = "languages" + case 1: + reuseID = "showbanner" + case 2: + reuseID = "showgetstarted" + default: + log.error("undefined indexPath(\rowType") + reuseID = "foo" + } + if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { + return cell + } + + let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) + + // Configure the cell... + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) + cell.selectedBackgroundView = selectionColor + switch(rowType) { + case 0: + cell.textLabel?.text = "Installed Languages" + cell.accessoryType = .disclosureIndicator + case 1: + cell.textLabel?.text = "Show Banner" + case 2: + cell.textLabel?.text = "Show 'Get Started' at startup'" + default: + log.error("undefined indexPath(\rowType") + } + + return cell + } + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override open func prepare(for segue: UIStoryboardSegue, sender: Any?) { + log.info("prepare for segue") + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + +} diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index 6b5f476c2a..f070b6b6fd 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -547,7 +547,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg _ = dismissDropDownMenu() popover?.dismiss(animated: false) - Manager.shared.manageLanguages(inVC: self) + Manager.shared.manageKeymanEngine(inVC: self) } @objc func actionButtonClick(_ sender: Any) { diff --git a/web/package-lock.json b/web/package-lock.json index 70240f8bf1..d268683e86 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1274,8 +1274,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1296,14 +1295,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1318,20 +1315,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1448,8 +1442,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1461,7 +1454,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1476,7 +1468,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1484,14 +1475,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1510,7 +1499,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1591,8 +1579,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1604,7 +1591,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -1690,8 +1676,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -1727,7 +1712,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1747,7 +1731,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1791,14 +1774,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, From b52f0e651bd51b3c08ce0e8f05992add759ef24c Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 20 May 2019 00:57:00 -0500 Subject: [PATCH 10/47] launch SettingsViewController without Navigator, can't back out yet --- .../KeymanEngine.xcodeproj/project.pbxproj | 32 +++- .../KeyboardNameTableViewCell.swift | 2 + .../LexicalModelInfoViewController.swift | 2 +- .../KMEI/KeymanEngine/Classes/Manager.swift | 33 +++- .../Classes/Settings/Settings.storyboard | 53 +----- .../Settings/SettingsViewController.swift | 165 ++++++++++++++---- .../SetUpViewController.xib | 15 +- 7 files changed, 203 insertions(+), 99 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index ea0a624c7f..634c5808c3 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -98,9 +98,23 @@ 9A31E2CE224AE8B100D9A491 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; 9A31E2CF224AE8B800D9A491 /* LexicalModelRepositoryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */; }; 9A31E2D0224AE8BF00D9A491 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; + 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62161F67CFB800268D03 /* KeyboardPickerViewController.swift */; }; + 9A3B14BB228F592F0052A11F /* LanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04C2A6C1F6B7D9A00BA42B6 /* LanguageViewController.swift */; }; + 9A3B14BC228F59380052A11F /* LanguageDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */; }; + 9A3B14BD228F593C0052A11F /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; + 9A3B14BE228F59420052A11F /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; + 9A3B14BF228F59450052A11F /* KeyboardInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62141F67C8D500268D03 /* KeyboardInfoViewController.swift */; }; + 9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; + 9A3B14C1228F595A0052A11F /* KeyboardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3A02098993900040A69 /* KeyboardError.swift */; }; + 9A3B14C3228F59C10052A11F /* KeyboardPickerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C620F1F67BD4500268D03 /* KeyboardPickerButton.swift */; }; + 9A3B14C4228F59C70052A11F /* KeyboardPickerBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C620B1F6792A200268D03 /* KeyboardPickerBarButtonItem.swift */; }; + 9A3B14C6228F59E00052A11F /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C040E50F1F8606E300901EE4 /* TextField.swift */; }; + 9A3B14C7228F59E40052A11F /* TextFieldDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8C1F87480700AF3785 /* TextFieldDelegateProxy.swift */; }; + 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8E1F8750B700AF3785 /* TextView.swift */; }; + 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B901F8763E100AF3785 /* TextViewDelegateProxy.swift */; }; + 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; }; 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; }; 9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; }; - 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A60763C22892485003BCFBA /* Settings.storyboard */; }; 9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; @@ -980,7 +994,6 @@ buildActionMask = 2147483647; files = ( C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */, - 9A60763D22892485003BCFBA /* Settings.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1056,6 +1069,10 @@ buildActionMask = 2147483647; files = ( 9A079E45223DBEF000581263 /* PopoverView.swift in Sources */, + 9A3B14C4228F59C70052A11F /* KeyboardPickerBarButtonItem.swift in Sources */, + 9A3B14BE228F59420052A11F /* LexicalModelInfoViewController.swift in Sources */, + 9A3B14C7228F59E40052A11F /* TextFieldDelegateProxy.swift in Sources */, + 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */, 9A31E2CC224AE89F00D9A491 /* NotificationCenter+Typed.swift in Sources */, 9A079E2C22361B4B00581263 /* KeyboardRepository.swift in Sources */, 9A31E2C8224AE87600D9A491 /* RegisteredFont.swift in Sources */, @@ -1071,16 +1088,21 @@ 9A079E46223DBEF900581263 /* KeyPreviewView.swift in Sources */, 9A079DE32231A16600581263 /* Constants.swift in Sources */, 9A31E2C1224AAD8300D9A491 /* FontManager.swift in Sources */, + 9A3B14C6228F59E00052A11F /* TextField.swift in Sources */, + 9A3B14C3228F59C10052A11F /* KeyboardPickerButton.swift in Sources */, 9A079E29223619F100581263 /* InputViewController.swift in Sources */, 9A079E28223619D300581263 /* Options.swift in Sources */, 9A079DE42231A1ED00581263 /* Log.swift in Sources */, 9A31E2C5224AE83D00D9A491 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift in Sources */, 9A079DDB2231A0F100581263 /* KMPKeyboard.swift in Sources */, + 9A3B14BD228F593C0052A11F /* LexicalModelPickerViewController.swift in Sources */, 9A079DDF2231A13A00581263 /* Keyboard.swift in Sources */, 9A31E2C2224AAD9A00D9A491 /* Notifications.swift in Sources */, 9A079DDD2231A11C00581263 /* InstallableKeyboard.swift in Sources */, 9A079DD2223194B100581263 /* KeymanEngineTests.swift in Sources */, 9A079E41223B602B00581263 /* LexicalModel.swift in Sources */, + 9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */, + 9A3B14BB228F592F0052A11F /* LanguageViewController.swift in Sources */, 9A31E2CE224AE8B100D9A491 /* LexicalModelRepository.swift in Sources */, 9A079E2E22361B8100581263 /* KeymanResponder+Types.swift in Sources */, 9A079DE12231A14D00581263 /* Storage.swift in Sources */, @@ -1090,17 +1112,23 @@ 9A31E2CB224AE89600D9A491 /* LexicalModelKeymanPackage.swift in Sources */, 9A31E2CF224AE8B800D9A491 /* LexicalModelRepositoryDelegate.swift in Sources */, 9A31E2C9224AE88600D9A491 /* LanguagesAPICall.swift in Sources */, + 9A3B14C1228F595A0052A11F /* KeyboardError.swift in Sources */, 9A079E2B22361A3200581263 /* KeymanWebViewController.swift in Sources */, 9A31E2CD224AE8A700D9A491 /* NotificationObserver.swift in Sources */, 9A079E382238680700581263 /* KMPLexicalModel.swift in Sources */, + 9A3B14BC228F59380052A11F /* LanguageDetailViewController.swift in Sources */, 9A079E2F22361BA100581263 /* Reachability.m in Sources */, 9A31E2C4224AB2A300D9A491 /* UIButton+Helpers.swift in Sources */, 9A079DDE2231A13100581263 /* FullKeyboardID.swift in Sources */, + 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */, 9A31E2CA224AE88E00D9A491 /* LexicalModelAPICall.swift in Sources */, 9A079E3222361C3B00581263 /* Migrations.swift in Sources */, 9A08255A227589360051EBB0 /* Formatter+ISODateExtension.swift in Sources */, + 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */, 9A079E2A223619FC00581263 /* KeymanWebDelegate.swift in Sources */, + 9A3B14BF228F59450052A11F /* KeyboardInfoViewController.swift in Sources */, 9A31E2C3224AADBB00D9A491 /* NotificationName.swift in Sources */, + 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */, 9A079DDA2231A0B900581263 /* KeymanPackage.swift in Sources */, 9A079DE22231A15900581263 /* Manager.swift in Sources */, 9A079E47223DBF0100581263 /* SubKeysView.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift index a5455043c4..5b71148716 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardNameTableViewCell.swift @@ -6,6 +6,8 @@ // Copyright © 2017 SIL International. All rights reserved. // +import UIKit + private let cellSelectedTag = 4576 // arbitrary number class KeyboardNameTableViewCell: UITableViewCell, UIAlertViewDelegate { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift index c97233f2d1..c6109e02e5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift @@ -5,7 +5,7 @@ // Created by Randy Boring on 3/19/19. // Copyright © 2019 SIL International. All rights reserved. // - +import UIKit import Foundation class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index ce170f4d4d..f81e485c23 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -216,7 +216,21 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // MARK: - Keyboard management public func manageKeymanEngine(inVC: UIViewController) -> Void { - let settingsVC = SettingsViewController(style: UITableViewStyle.grouped) + +// for b in Bundle.allBundles { +// do { +// let sb = try UIStoryboard(name: "Settings", bundle: nil) +// if let vc = sb.instantiateInitialViewController() { +// inVC.present(vc, animated: true, completion: { +// log.info("presented settings") +// }) +// } +// } catch { +// log.info("bundle \(b) does not contain Settings.storyboard") +// } +// } + + let settingsVC = SettingsViewController() // let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) // navigationController?.pushViewController(languagesVC, animated: true) @@ -224,15 +238,16 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // if UIDevice.current.userInterfaceIdiom == .phone { // present(languagesVC, animated: true, completion: nil) // } else { - let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", - preferredStyle: UIAlertControllerStyle.alert) - alertController.addAction(UIAlertAction(title: "Cancel", - style: UIAlertActionStyle.cancel, - handler: nil)) - alertController.addAction(UIAlertAction(title: "OK", - style: UIAlertActionStyle.default, - handler: nil)) +// let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", +// preferredStyle: UIAlertControllerStyle.alert) +// alertController.addAction(UIAlertAction(title: "Cancel", +// style: UIAlertActionStyle.cancel, +// handler: nil)) +// alertController.addAction(UIAlertAction(title: "OK", +// style: UIAlertActionStyle.default, +// handler: nil)) +// inVC.present(settingsVC, animated: true, completion: nil) // let popover = UIPopoverController(contentViewController: languagesVC) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard index 1a2e72baeb..c5b79081f8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/Settings.storyboard @@ -6,29 +6,28 @@ - - - + + - + - - + @@ -106,43 +105,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 08e00985cf..ff253ac124 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -9,16 +9,64 @@ import UIKit open class SettingsViewController: UITableViewController { + private var itemsArray = [[String: String]]() + private var userLanguages: [String] = [String]() + override open func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + loadUserLanguages() + } + override open func viewDidLoad() { - super.viewDidLoad() + super.viewDidLoad() + + navigationController?.toolbar?.barTintColor = UIColor(red: 0.5, green: 0.75, + blue: 0.25, alpha: 0.9) - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + open func launchSettings(launchingVC: UIViewController, sender: Any?) -> Void { + let sb : UIStoryboard = UIStoryboard(name: "Settings", bundle: nil) + if let vc = sb.instantiateInitialViewController() { + launchingVC.present(vc, animated: true, completion: { + log.info("presented settings") + }) } + } +// +// convenience init() { +// self.init(style: UITableViewStyle.grouped) +// } + + init(/*storage: Storage*/) { +// self.storage = storage + super.init(nibName: nil, bundle: nil) + + itemsArray = [[String: String]]() + itemsArray.append([ + "title": "Installed Languages...", + "subtitle": "0", //count of installed languages as string + "reuseid" : "language" + ]) + + itemsArray.append([ + "title": "Show Banner", + "subtitle": "", + "reuseid" : "showbanner" + ]) + + itemsArray.append([ + "title": "Show 'Get Started' at startup", + "subtitle": "", + "reuseid" : "showgetstarted" + ]) + _ = view + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } // MARK: - Table view data source @@ -33,43 +81,80 @@ open class SettingsViewController: UITableViewController { } override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var reuseID: String = "foo" - let lastIndex = indexPath.index(before: indexPath.endIndex) - let rowType = indexPath[lastIndex] - switch(rowType) { - case 0: - reuseID = "languages" - case 1: - reuseID = "showbanner" - case 2: - reuseID = "showgetstarted" - default: - log.error("undefined indexPath(\rowType") - reuseID = "foo" - } - if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { + let cellIdentifier = itemsArray[indexPath.row]["reuseid"] + if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier!) { return cell } + let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) + cell.selectionStyle = .none - let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) - - // Configure the cell... - let selectionColor = UIView() - selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) - cell.selectedBackgroundView = selectionColor - switch(rowType) { - case 0: - cell.textLabel?.text = "Installed Languages" + switch(cellIdentifier) { + case "languages": cell.accessoryType = .disclosureIndicator - case 1: - cell.textLabel?.text = "Show Banner" - case 2: - cell.textLabel?.text = "Show 'Get Started' at startup'" + case "showbanner": + cell.accessoryType = .none + case "showgetstarted": + cell.accessoryType = .none default: - log.error("undefined indexPath(\rowType") + log.error("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")") + cell.accessoryType = .none + } + + return cell + } + + override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + cell.selectionStyle = .none + cell.accessoryType = .none + cell.textLabel?.text = itemsArray[indexPath.row]["title"] + cell.detailTextLabel?.text = itemsArray[indexPath.row]["subtitle"] + cell.tag = indexPath.row + + if indexPath.row == 0 { + cell.accessoryType = .disclosureIndicator + } else { + cell.isUserInteractionEnabled = false + cell.textLabel?.isEnabled = true + cell.detailTextLabel?.isEnabled = false } - return cell +// var reuseID: String = "foo" +// let lastIndex = indexPath.index(before: indexPath.endIndex) +// let rowType = indexPath[lastIndex] +// switch(rowType) { +// case 0: +// reuseID = "languages" +// case 1: +// reuseID = "showbanner" +// case 2: +// reuseID = "showgetstarted" +// default: +// log.error("undefined indexPath(\rowType") +// reuseID = "foo" +// } +// if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { +// return cell +// } +// +// let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) +// +// // Configure the cell... +// let selectionColor = UIView() +// selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) +// cell.selectedBackgroundView = selectionColor +// switch(rowType) { +// case 0: +// cell.textLabel?.text = "Installed Languages2" +// cell.accessoryType = .disclosureIndicator +// case 1: +// cell.textLabel?.text = "Show Banner" +// case 2: +// cell.textLabel?.text = "Show 'Get Started' at startup'" +// default: +// log.error("undefined indexPath(\rowType") +// } +// +// return cell } /* @@ -116,4 +201,12 @@ open class SettingsViewController: UITableViewController { // Pass the selected object to the new view controller. } + // MARK: - language access - + private func loadUserLanguages() { + //iterate the list of installed languages and save their names + userLanguages = ["en"] + + itemsArray[0]["subtitle"] = String(userLanguages.count) + } + } diff --git a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib index c849610801..a53d2a7972 100644 --- a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib +++ b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.xib @@ -1,7 +1,12 @@ - - + + + + + - + + + @@ -25,10 +30,10 @@ - + - + From 01c88c7ac3ae143442ad57b353eb5556245fbd66 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 22 May 2019 21:54:15 -0500 Subject: [PATCH 11/47] put Done button at top of Keyman Settings in viewDidLoad --- .../KeymanEngine.xcodeproj/project.pbxproj | 6 + .../KMEI/KeymanEngine/Classes/Manager.swift | 46 +-- .../InstalledLanguagesViewController.swift | 376 ++++++++++++++++++ .../Settings/SettingsViewController.swift | 33 +- .../Keyman/Keyman/MainViewController.swift | 2 +- 5 files changed, 422 insertions(+), 41 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 634c5808c3..33e52cfc07 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -113,6 +113,8 @@ 9A3B14C8228F59EE0052A11F /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B8E1F8750B700AF3785 /* TextView.swift */; }; 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0324B901F8763E100AF3785 /* TextViewDelegateProxy.swift */; }; 9A3B14CA228F5AF60052A11F /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; }; + 9A3B14D2229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */; }; + 9A3B14D3229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.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 */; }; @@ -304,6 +306,7 @@ 9A079E3F223B602B00581263 /* LexicalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModel.swift; sourceTree = ""; }; 9A079E42223B61AE00581263 /* FullLexicalModelID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullLexicalModelID.swift; sourceTree = ""; }; 9A082558227589360051EBB0 /* Formatter+ISODateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Formatter+ISODateExtension.swift"; sourceTree = ""; }; + 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.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 = ""; }; @@ -589,6 +592,7 @@ children = ( 9A60763C22892485003BCFBA /* Settings.storyboard */, 9A60764322893A4E003BCFBA /* SettingsViewController.swift */, + 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */, ); name = Settings; sourceTree = ""; @@ -1123,6 +1127,7 @@ 9A3B14C9228F59F10052A11F /* TextViewDelegateProxy.swift in Sources */, 9A31E2CA224AE88E00D9A491 /* LexicalModelAPICall.swift in Sources */, 9A079E3222361C3B00581263 /* Migrations.swift in Sources */, + 9A3B14D3229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */, 9A08255A227589360051EBB0 /* Formatter+ISODateExtension.swift in Sources */, 9A3B14BA228D333E0052A11F /* KeyboardPickerViewController.swift in Sources */, 9A079E2A223619FC00581263 /* KeymanWebDelegate.swift in Sources */, @@ -1190,6 +1195,7 @@ C06D37431F81F5C400F61AE0 /* KeyboardPickerBarButtonItem.swift in Sources */, C082CE151F90AFD400860F02 /* Collection+SafeAccess.swift in Sources */, C06D37441F81F5C400F61AE0 /* KeyboardNameTableViewCell.swift in Sources */, + 9A3B14D2229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */, C07A9D8A1FD1762700828ADD /* KeyboardRepository.swift in Sources */, C08E69911FDA6F6F0026056B /* FullKeyboardID.swift in Sources */, 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index f81e485c23..c9a5191d70 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -214,46 +214,14 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat } // MARK: - Keyboard management - - public func manageKeymanEngine(inVC: UIViewController) -> Void { - -// for b in Bundle.allBundles { -// do { -// let sb = try UIStoryboard(name: "Settings", bundle: nil) -// if let vc = sb.instantiateInitialViewController() { -// inVC.present(vc, animated: true, completion: { -// log.info("presented settings") -// }) -// } -// } catch { -// log.info("bundle \(b) does not contain Settings.storyboard") -// } -// } - + + public func showKeymanEngineSettings(inVC: UIViewController) -> Void { + hideKeyboard() let settingsVC = SettingsViewController() -// let languagesVC = UIAlertController.init(title: "Languages Settings", message: "Languages with Keyboards should be shown here", preferredStyle: .actionSheet) -// navigationController?.pushViewController(languagesVC, animated: true) - -// textView.isUserInteractionEnabled = false -// if UIDevice.current.userInterfaceIdiom == .phone { -// present(languagesVC, animated: true, completion: nil) -// } else { - -// let alertController = UIAlertController(title: "Settings", message: "Keyman Engine settings", -// preferredStyle: UIAlertControllerStyle.alert) -// alertController.addAction(UIAlertAction(title: "Cancel", -// style: UIAlertActionStyle.cancel, -// handler: nil)) -// alertController.addAction(UIAlertAction(title: "OK", -// style: UIAlertActionStyle.default, -// handler: nil)) -// - inVC.present(settingsVC, animated: true, completion: nil) - -// let popover = UIPopoverController(contentViewController: languagesVC) -// popover!.present(from CGRect(inView.frame), in inView, permittedArrowDirections arrowDirections: UIPopoverArrowDirectionAny, animated: true) -// (from: (navigationItem.rightBarButtonItems?[8])!, permittedArrowDirections: .any, animated: true) -// } + let nc = UINavigationController(rootViewController: settingsVC) + nc.modalTransitionStyle = .coverVertical + nc.modalPresentationStyle = .pageSheet + inVC.present(nc, animated: true) } /// Sets the current keyboard, querying from the user's list of keyboards. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift new file mode 100644 index 0000000000..69c640ce11 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -0,0 +1,376 @@ +// +// InstalledLanguagesViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 5/20/19. +// Copyright © 2019 SIL International. All rights reserved. +// +// +import QuartzCore +import UIKit + +private let activityViewTag = -2 +private let toolbarButtonTag = 100 +private let toolbarLabelTag = 101 +private let toolbarActivityIndicatorTag = 102 + +class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelegate { + private var userKeyboards: [String: InstallableKeyboard] = [:] + private var userLexicalModels: [String: InstallableLexicalModel] = [:] + private var sectionIndexTitles: [String] = [] + private var indices: [Int] = [] + private var selectedSection = 0 + private var isUpdate = false + private var languages: [Language] = [] + private let keyboardRepository: KeyboardRepository? + private let lexicalModelRepository: LexicalModelRepository? + + private var keyboardDownloadStartedObserver: NotificationObserver? + private var keyboardDownloadFailedObserver: NotificationObserver? + + init(_ keyboardRepository: KeyboardRepository) { + self.keyboardRepository = keyboardRepository + self.lexicalModelRepository = nil + super.init(nibName: nil, bundle: nil) + keyboardRepository.delegate = self + } + + init(_ lexicalModelRepository: LexicalModelRepository) { + self.lexicalModelRepository = lexicalModelRepository + self.keyboardRepository = nil + super.init(nibName: nil, bundle: nil) + lexicalModelRepository.delegate = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + super.loadView() + if let languageDict = keyboardRepository?.languages { + languages = languageList(languageDict) + } else { + keyboardRepository?.fetch() + } + + loadUserKeyboards() + } + + override func viewDidLoad() { + super.viewDidLoad() + title = "Add New Keyboard" + selectedSection = NSNotFound + keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( + forName: Notifications.keyboardDownloadStarted, + observer: self, + function: InstalledLanguagesViewController.keyboardDownloadStarted) + keyboardDownloadFailedObserver = NotificationCenter.default.addObserver( + forName: Notifications.keyboardDownloadFailed, + observer: self, + function: InstalledLanguagesViewController.keyboardDownloadFailed) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + navigationController?.setToolbarHidden(true, animated: true) + // if no rows to show yet, show a loading indicator + if numberOfSections(in: tableView) == 0 { + showActivityView() + } + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return languages.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellIdentifierType1 = "CellType1" + let cellIdentifierType2 = "CellType2" + let keyboards = languages[indexPath.section].keyboards! + let cellIdentifier = (keyboards.count < 2) ? cellIdentifierType1 : cellIdentifierType2 + let cell: UITableViewCell + if let reusedCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { + cell = reusedCell + } else { + if keyboards.count < 2 { + 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 + } else { + cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier) + cell.accessoryType = .disclosureIndicator + 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 + } + } + cell.detailTextLabel?.text = keyboards.count < 2 ? (keyboards.first?.name ?? "") : "" + return cell + } + + // Create sections by first letter + override func sectionIndexTitles(for tableView: UITableView) -> [String] { + if !sectionIndexTitles.isEmpty { + return sectionIndexTitles + } + + sectionIndexTitles = [] + indices = [] + for (index, language) in languages.enumerated() { + let firstLetter = String(language.name.prefix(1)).uppercased() + if !sectionIndexTitles.contains(firstLetter) { + sectionIndexTitles.append(firstLetter) + indices.append(index) + } + } + return sectionIndexTitles + } + + override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + return indices[index] + } + + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + guard let language = languages[safe: indexPath.section] else { + return + } + + cell.textLabel?.text = language.name + + if cell.accessoryType == .disclosureIndicator { + return + } + guard let cell = cell as? KeyboardNameTableViewCell else { + return + } + + let languageID = language.id + let keyboardID = language.keyboards![0].id + + if isAdded(languageID: languageID, keyboardID: keyboardID) { + 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.stateForKeyboard(withID: keyboardID) + cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + selectedSection = indexPath.section + tableView.cellForRow(at: indexPath)?.isSelected = false + if tableView.cellForRow(at: indexPath)?.accessoryType == .disclosureIndicator { + let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" + showLanguageDetailView(title: title, languageIndex: indexPath.section) + return + } + + let language = languages[indexPath.section] + let keyboardIndex = 0; + let keyboard = language.keyboards![keyboardIndex] + + let state = Manager.shared.stateForKeyboard(withID: keyboard.id) + if state != .downloading { + isUpdate = state != .needsDownload + let alertController = UIAlertController(title: "\(language.name): \(keyboard.name)", + message: "Would you like to download this keyboard?", + 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(keyboardIndex)} )) + self.present(alertController, animated: true, completion: nil) + } + } + + override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { + let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" + showLanguageDetailView(title: title, languageIndex: indexPath.section) + } + + private func showLanguageDetailView(title: String, languageIndex: Int) { + let langDetailView = LanguageDetailViewController(language: languages[languageIndex]) + langDetailView.title = title + navigationController?.pushViewController(langDetailView, animated: true) + } + + func errorAcknowledgmentHandler(withAction action: UIAlertAction) { + if !languages.isEmpty { + navigationController?.popToRootViewController(animated: true) + } + } + + func downloadHandler(_ keyboardIndex: Int) { + let language = languages[selectedSection] + let keyboard = language.keyboards![keyboardIndex] + Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate) + } + + private func keyboardDownloadStarted() { + view.isUserInteractionEnabled = false + navigationItem.setHidesBackButton(true, animated: true) + + guard let toolbar = navigationController?.toolbar else { + return + } + + let labelFrame = CGRect(origin: toolbar.frame.origin, + size: CGSize(width: toolbar.frame.width * 0.95, + height: toolbar.frame.height * 0.7)) + let label = UILabel(frame: labelFrame) + label.backgroundColor = UIColor.clear + label.textColor = UIColor.white + label.textAlignment = .center + label.center = CGPoint(x: toolbar.frame.width * 0.5, y: toolbar.frame.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: toolbar.frame.width - indicatorView.frame.width, + y: toolbar.frame.height * 0.5) + indicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin] + indicatorView.tag = toolbarActivityIndicatorTag + indicatorView.startAnimating() + toolbar.viewWithTag(toolbarButtonTag)?.removeFromSuperview() + toolbar.viewWithTag(toolbarLabelTag)?.removeFromSuperview() + toolbar.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() + toolbar.addSubview(label) + toolbar.addSubview(indicatorView) + navigationController?.setToolbarHidden(false, animated: true) + } + + private func keyboardDownloadFailed() { + view.isUserInteractionEnabled = true + navigationItem.setHidesBackButton(false, animated: true) + } + + func showActivityView() { + view.isUserInteractionEnabled = false + let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) + let activityView = UIView(frame: indicatorView.bounds.insetBy(dx: -10.0, dy: -10.0)) + activityView.backgroundColor = UIColor(white: 0.5, alpha: 0.8) + activityView.layer.cornerRadius = 6.0 + activityView.center = view.center + activityView.tag = activityViewTag + activityView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, + .flexibleBottomMargin] + + indicatorView.center = CGPoint(x: activityView.bounds.size.width * 0.5, y: activityView.bounds.size.height * 0.5) + indicatorView.startAnimating() + activityView.addSubview(indicatorView) + view.addSubview(activityView) + } + + func dismissActivityView() { + let activityView = view.viewWithTag(activityViewTag) + activityView?.removeFromSuperview() + view.isUserInteractionEnabled = true + } + + func loadUserKeyboards() { + userKeyboards = [:] + guard let userKbList = Storage.active.userDefaults.userKeyboards else { + return + } + + for kb in userKbList { + let dictKey = "\(kb.languageID)_\(kb.id)" + userKeyboards[dictKey] = kb + } + } + + func loadUserLexicalModels() { + userLexicalModels = [:] + guard let userLmList = Storage.active.userDefaults.userLexicalModels else { + return + } + + for lm in userLmList { + let dictKey = "\(lm.languageID)_\(lm.id)" + userLexicalModels[dictKey] = lm + } + } + + private func isAdded(languageID: String?, keyboardID: String?) -> Bool { + guard let languageID = languageID, let keyboardID = keyboardID else { + return false + } + return userKeyboards["\(languageID)_\(keyboardID)"] != nil + } + + private func showConnectionErrorAlert() { + dismissActivityView() + let alertController = UIAlertController(title: "Connection Error", + message: "Could not reach Keyman server. Please try again later.", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.default, + handler: errorAcknowledgmentHandler)) + + self.present(alertController, animated: true, completion: nil) + } +} + +// MARK: - KeyboardRepositoryDelegate +extension InstalledLanguagesViewController: KeyboardRepositoryDelegate { + func keyboardRepositoryDidFetch(_ repository: KeyboardRepository) { + if let languageDict = repository.languages { + languages = languageList(languageDict) + } + self.dismissActivityView() + self.tableView.reloadData() + if self.numberOfSections(in: self.tableView) == 0 { + self.showConnectionErrorAlert() + } + } + + func keyboardRepository(_ repository: KeyboardRepository, didFailFetch error: Error) { + dismissActivityView() + showConnectionErrorAlert() + } + + private func languageList(_ languageDict: [String: Language]) -> [Language] { + return languageDict.values.sorted { a, b -> Bool in + a.name.localizedCaseInsensitiveCompare(b.name) == .orderedAscending + } + } +} + +// MARK: - LexicalModelRepositoryDelegate +//may not need this, as we don't plan ever to fetch the whole lexical model repository (or even the list of all available) +extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { + func lexicalModelRepositoryDidFetch(_ repository: LexicalModelRepository) { + if let languageDict = repository.languages { + languages = languageList(languageDict) + } + self.dismissActivityView() + self.tableView.reloadData() + if self.numberOfSections(in: self.tableView) == 0 { + self.showConnectionErrorAlert() + } + } + + func lexicalModelRepository(_ repository: LexicalModelRepository, didFailFetch error: Error) { + dismissActivityView() + showConnectionErrorAlert() + } +} + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index ff253ac124..d7281ddaa1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -21,9 +21,17 @@ open class SettingsViewController: UITableViewController { override open func viewDidLoad() { super.viewDidLoad() + title = "Keyman Settings" + let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + action: #selector(self.doneClicked)) + navigationItem.leftBarButtonItem = doneButton + navigationController?.toolbar?.barTintColor = UIColor(red: 0.5, green: 0.75, blue: 0.25, alpha: 0.9) - + } + + @objc func doneClicked(_ sender: Any) { + Manager.shared.dismissKeyboardPicker(self) } open func launchSettings(launchingVC: UIViewController, sender: Any?) -> Void { @@ -209,4 +217,27 @@ open class SettingsViewController: UITableViewController { itemsArray[0]["subtitle"] = String(userLanguages.count) } + public func setIsDoneButtonEnabled(_ nc: UINavigationController, _ value: Bool) { + let doneOrCancel = value + if doneOrCancel { + let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + action: nil /* #selector(self.doneClicked) */ ) + nc.navigationItem.leftBarButtonItem = doneButton + } else { + let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, + action: nil /* #selector(self.cancelClicked) */ ) + nc.navigationItem.leftBarButtonItem = cancelButton + } + } + + func showLanguages() { + let vc = InstalledLanguagesViewController(Manager.shared.apiKeyboardRepository) + if let nc = navigationController { + nc.pushViewController(vc, animated: true) + setIsDoneButtonEnabled(nc, true) + } else { + log.error("no navigation controller for showing languages???") + } + } + } diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift index f070b6b6fd..17373dc30a 100644 --- a/ios/keyman/Keyman/Keyman/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -547,7 +547,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg _ = dismissDropDownMenu() popover?.dismiss(animated: false) - Manager.shared.manageKeymanEngine(inVC: self) + Manager.shared.showKeymanEngineSettings(inVC: self) } @objc func actionButtonClick(_ sender: Any) { From 375a0890a3ea74326df28b55f99755f1988e51de Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Fri, 24 May 2019 00:30:59 -0500 Subject: [PATCH 12/47] clicking InstalledLanguages shows InstalledLanguagesViewController count the installed languages navigate back to Settings --- .../InstalledLanguagesViewController.swift | 24 +++------- .../Settings/SettingsViewController.swift | 47 +++++++++++++++++-- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 69c640ce11..30b9822433 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -22,24 +22,19 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega private var selectedSection = 0 private var isUpdate = false private var languages: [Language] = [] + private var installedLanguages: [String: Language] private let keyboardRepository: KeyboardRepository? private let lexicalModelRepository: LexicalModelRepository? private var keyboardDownloadStartedObserver: NotificationObserver? private var keyboardDownloadFailedObserver: NotificationObserver? - init(_ keyboardRepository: KeyboardRepository) { - self.keyboardRepository = keyboardRepository + init(_ givenLanguages: [String: Language]) { + self.installedLanguages = givenLanguages + self.keyboardRepository = nil self.lexicalModelRepository = nil super.init(nibName: nil, bundle: nil) - keyboardRepository.delegate = self - } - - init(_ lexicalModelRepository: LexicalModelRepository) { - self.lexicalModelRepository = lexicalModelRepository - self.keyboardRepository = nil - super.init(nibName: nil, bundle: nil) - lexicalModelRepository.delegate = self +// keyboardRepository.delegate = self } required init?(coder aDecoder: NSCoder) { @@ -48,18 +43,13 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega override func loadView() { super.loadView() - if let languageDict = keyboardRepository?.languages { - languages = languageList(languageDict) - } else { - keyboardRepository?.fetch() - } - + languages = languageList(installedLanguages) loadUserKeyboards() } override func viewDidLoad() { super.viewDidLoad() - title = "Add New Keyboard" + title = "Installed Languages" selectedSection = NSNotFound keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( forName: Notifications.keyboardDownloadStarted, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index d7281ddaa1..5aa92b59ff 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -10,7 +10,7 @@ import UIKit open class SettingsViewController: UITableViewController { private var itemsArray = [[String: String]]() - private var userLanguages: [String] = [String]() + private var userLanguages: [String: Language] = [:] override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -163,6 +163,25 @@ open class SettingsViewController: UITableViewController { // } // // return cell + } + + // In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath: + override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.cellForRow(at: indexPath)?.isSelected = false + performAction(for: indexPath) + } + + override open func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { + performAction(for: indexPath) + } + + private func performAction(for indexPath: IndexPath) { + switch indexPath.section { + case 0: + showLanguages() + default: + break + } } /* @@ -212,8 +231,28 @@ open class SettingsViewController: UITableViewController { // MARK: - language access - private func loadUserLanguages() { //iterate the list of installed languages and save their names - userLanguages = ["en"] - + // Get keyboards list if it exists in user defaults, otherwise create a new one + let userDefaults = Storage.active.userDefaults + let userKeyboards = userDefaults.userKeyboards ?? [] + + var keyboardLanguages = [String: Language]() + for k in userKeyboards { + let l = k.languageID + keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)], lexicalModels: nil, font: nil, oskFont: nil) + } + // there shouldn't be any lexical models for languages that don't have a keyboard installed + // but check + let userLexicalModels = userDefaults.userLexicalModels ?? [] + for lm in userLexicalModels { + let l = lm.languageID + if let langName = keyboardLanguages[l]?.name { + log.info("keyboard language \(l) \(langName) has lexical model") + } else { + log.error("lexical model language \(l) has no keyboard installed!") + } + } + + userLanguages = keyboardLanguages itemsArray[0]["subtitle"] = String(userLanguages.count) } @@ -231,7 +270,7 @@ open class SettingsViewController: UITableViewController { } func showLanguages() { - let vc = InstalledLanguagesViewController(Manager.shared.apiKeyboardRepository) + let vc = InstalledLanguagesViewController(userLanguages) if let nc = navigationController { nc.pushViewController(vc, animated: true) setIsDoneButtonEnabled(nc, true) From 232c32126632ca28139669ae563618e3ec080812 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Fri, 24 May 2019 21:52:19 -0500 Subject: [PATCH 13/47] rename Installed Languages to Language Settings --- .../Classes/Settings/InstalledLanguagesViewController.swift | 2 +- .../KeymanEngine/Classes/Settings/SettingsViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 30b9822433..0b5a78b7a7 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -49,7 +49,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega override func viewDidLoad() { super.viewDidLoad() - title = "Installed Languages" + title = "Languages Settings" selectedSection = NSNotFound keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( forName: Notifications.keyboardDownloadStarted, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 5aa92b59ff..c95c0a645d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -53,7 +53,7 @@ open class SettingsViewController: UITableViewController { itemsArray = [[String: String]]() itemsArray.append([ - "title": "Installed Languages...", + "title": "Languages Settings", "subtitle": "0", //count of installed languages as string "reuseid" : "language" ]) From fed3692891e5a1a07dc5ed3af23b1b040ac62f69 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Tue, 28 May 2019 18:28:45 -0500 Subject: [PATCH 14/47] banner and getstarted switches show, now. get started switch even works mostly banner switch needs to be attached to a setting still. --- .../Settings/SettingsViewController.swift | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index c95c0a645d..fb15dd3211 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -87,6 +87,21 @@ open class SettingsViewController: UITableViewController { // #warning Incomplete implementation, return the number of rows return 3 } + + public func frameAtRightOfCell(cell cellFrame: CGRect, controlSize: CGSize) -> CGRect { + let rightOffset = cellFrame.size.width + let switchWidth: CGFloat = 20 + let switchX = rightOffset - switchWidth + let switchHeight = controlSize.height + let cellSwitchHeightDiff = cellFrame.size.height - switchHeight + let switchY = cellFrame.origin.y + 0.5 * cellSwitchHeightDiff + + let switchFrame = CGRect(x: switchX, + y: switchY, + width: switchWidth, + height: cellFrame.size.height) + return switchFrame + } override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cellIdentifier = itemsArray[indexPath.row]["reuseid"] @@ -101,8 +116,35 @@ open class SettingsViewController: UITableViewController { cell.accessoryType = .disclosureIndicator case "showbanner": cell.accessoryType = .none + let showBannerSwitch = UISwitch() + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: showBannerSwitch.frame.size) + showBannerSwitch.frame = switchFrame + showBannerSwitch.isOn = false //TODO: find the setting this is to show! + showBannerSwitch.addTarget(self, action: #selector(self.bannerSwitchValueChanged), + for: .valueChanged) + cell.addSubview(showBannerSwitch) case "showgetstarted": cell.accessoryType = .none + let dontShowAgainSwitch = UISwitch() +// let rightOffset = cell.frame.size.width +// let switchWidth: CGFloat = 20 +// let switchX = rightOffset - switchWidth +// let dontShowAgainSwitch = UISwitch() +// let switchHeight = dontShowAgainSwitch.frame.size.height +// let cellSwitchHeightDiff = cell.frame.size.height - switchHeight +// let switchY = cell.frame.origin.y + 0.5 * cellSwitchHeightDiff + +// let switchFrame = CGRect(x: switchX, +// y: switchY, +// width: switchWidth, +// height: cell.frame.size.height) + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: dontShowAgainSwitch.frame.size) + dontShowAgainSwitch.frame = switchFrame + dontShowAgainSwitch.isOn = dontShowGetStarted + dontShowAgainSwitch.addTarget(self, action: #selector(self.showGetStartedSwitchValueChanged), + for: .valueChanged) + cell.addSubview(dontShowAgainSwitch) + default: log.error("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")") cell.accessoryType = .none @@ -110,6 +152,27 @@ open class SettingsViewController: UITableViewController { return cell } + + @objc func bannerSwitchValueChanged(_ sender: Any) { + let userData = Storage.active.userDefaults + if let toggle = sender as? UISwitch { + userData.set(toggle.isOn, forKey: "ShouldShowBanner") //??? + userData.synchronize() + } + } + + @objc func showGetStartedSwitchValueChanged(_ sender: Any) { + let userData = Storage.active.userDefaults + if let toggle = sender as? UISwitch { + userData.set(toggle.isOn, forKey: "ShouldShowGetStarted") + userData.synchronize() + } + } + + private var dontShowGetStarted: Bool { + let userData = Storage.active.userDefaults + return !userData.bool(forKey: "ShouldShowGetStarted") + } override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.selectionStyle = .none @@ -117,52 +180,15 @@ open class SettingsViewController: UITableViewController { cell.textLabel?.text = itemsArray[indexPath.row]["title"] cell.detailTextLabel?.text = itemsArray[indexPath.row]["subtitle"] cell.tag = indexPath.row - + cell.isUserInteractionEnabled = true + if indexPath.row == 0 { cell.accessoryType = .disclosureIndicator } else { - cell.isUserInteractionEnabled = false cell.textLabel?.isEnabled = true cell.detailTextLabel?.isEnabled = false } -// var reuseID: String = "foo" -// let lastIndex = indexPath.index(before: indexPath.endIndex) -// let rowType = indexPath[lastIndex] -// switch(rowType) { -// case 0: -// reuseID = "languages" -// case 1: -// reuseID = "showbanner" -// case 2: -// reuseID = "showgetstarted" -// default: -// log.error("undefined indexPath(\rowType") -// reuseID = "foo" -// } -// if let cell = tableView.dequeueReusableCell(withIdentifier: reuseID) { -// return cell -// } -// -// let cell = UITableViewCell(style: .default, reuseIdentifier: reuseID) -// -// // Configure the cell... -// let selectionColor = UIView() -// selectionColor.backgroundColor = UIColor(red: 74.0 / 255.0, green: 186.0 / 255.0, blue: 208.0 / 255.0, alpha: 1.0) -// cell.selectedBackgroundView = selectionColor -// switch(rowType) { -// case 0: -// cell.textLabel?.text = "Installed Languages2" -// cell.accessoryType = .disclosureIndicator -// case 1: -// cell.textLabel?.text = "Show Banner" -// case 2: -// cell.textLabel?.text = "Show 'Get Started' at startup'" -// default: -// log.error("undefined indexPath(\rowType") -// } -// -// return cell } // In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath: From c08c0dde6323c71db9a8d9454542d0b5aeff02ec Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 29 May 2019 16:27:17 -0500 Subject: [PATCH 15/47] fix crash when there is no model to download for the language of the newly downloaded keyboard --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index c9a5191d70..419d456551 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -1028,6 +1028,10 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat if let error = error { log.info("Failed to fetch lexical model list for "+languageID+". error: "+(error as! String)) self.downloadFailed(forLanguageID: languageID, error: error) //???forKeyboards + } else if nil == lexicalModels { + log.info("No lexical models available for language \(languageID) (nil)") + } else if 0 == lexicalModels?.count { + log.info("No lexical models available for language \(languageID) (empty)") } else { log.info("Fetched lexical model list for "+languageID+".") // choose which of the lexical models to download From 769138766998e7a1860c448fb0853c9237acaeb1 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 30 May 2019 00:14:18 -0500 Subject: [PATCH 16/47] begin Language Settings controller crashes due to not making any cells yet --- .../KeymanEngine.xcodeproj/project.pbxproj | 8 ++ .../InstalledLanguagesViewController.swift | 72 +++++------- .../LanguageSettingsViewController.swift | 104 ++++++++++++++++++ .../LanguageSettingsViewController.xib | 30 +++++ .../Settings/SettingsViewController.swift | 12 +- 5 files changed, 181 insertions(+), 45 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.xib diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 33e52cfc07..6350512f4c 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -122,6 +122,8 @@ 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A9CB084224170F700231FB9 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; 9A9CB0862241717C00231FB9 /* LexicalModelRepositoryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */; }; + 9AD4F53C229F85AC007992D3 /* LanguageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */; }; + 9AD4F53D229F85AC007992D3 /* LanguageSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */; }; C007C4651F9F52D8006461B9 /* LanguagesAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = C007C4641F9F52D8006461B9 /* LanguagesAPICall.swift */; }; C024C9941FA6EB470060583B /* NotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9931FA6EB470060583B /* NotificationName.swift */; }; C024C9961FA6EC650060583B /* NotificationCenter+Typed.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9951FA6EC650060583B /* NotificationCenter+Typed.swift */; }; @@ -315,6 +317,8 @@ 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APILexicalModelRepository.swift; sourceTree = ""; }; 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepository.swift; sourceTree = ""; }; 9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepositoryDelegate.swift; sourceTree = ""; }; + 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LanguageSettingsViewController.swift; path = Settings/LanguageSettingsViewController.swift; sourceTree = ""; }; + 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = LanguageSettingsViewController.xib; path = Settings/LanguageSettingsViewController.xib; sourceTree = ""; }; C007C4641F9F52D8006461B9 /* LanguagesAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagesAPICall.swift; sourceTree = ""; }; C024C9931FA6EB470060583B /* NotificationName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationName.swift; sourceTree = ""; }; C024C9951FA6EC650060583B /* NotificationCenter+Typed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Typed.swift"; sourceTree = ""; }; @@ -593,6 +597,8 @@ 9A60763C22892485003BCFBA /* Settings.storyboard */, 9A60764322893A4E003BCFBA /* SettingsViewController.swift */, 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */, + 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */, + 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */, ); name = Settings; sourceTree = ""; @@ -998,6 +1004,7 @@ buildActionMask = 2147483647; files = ( C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */, + 9AD4F53D229F85AC007992D3 /* LanguageSettingsViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1166,6 +1173,7 @@ C06D37361F81F5C300F61AE0 /* KeyboardMenuView.swift in Sources */, CE67D961228A6F190029F2B5 /* KeyboardCommandStructs.swift in Sources */, 9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */, + 9AD4F53C229F85AC007992D3 /* LanguageSettingsViewController.swift in Sources */, 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */, 9A079E3D223B5FAF00581263 /* InstallableLexicalModel.swift in Sources */, C06D37381F81F5C400F61AE0 /* SubKeysView.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 0b5a78b7a7..dc6395f47f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -87,22 +87,18 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega if let reusedCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) { cell = reusedCell } else { + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 204.0 / 255.0, green: 136.0 / 255.0, + blue: 34.0 / 255.0, alpha: 1.0) if keyboards.count < 2 { 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 } else { - cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier) - cell.accessoryType = .disclosureIndicator - 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 + cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) } + cell.selectedBackgroundView = selectionColor + cell.accessoryType = .disclosureIndicator } - cell.detailTextLabel?.text = keyboards.count < 2 ? (keyboards.first?.name ?? "") : "" + cell.detailTextLabel?.text = keyboards.count < 2 ? (keyboards.first?.name ?? "") : "\(keyboards.count) installed" return cell } @@ -142,20 +138,13 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega return } - let languageID = language.id let keyboardID = language.keyboards![0].id - if isAdded(languageID: languageID, keyboardID: keyboardID) { - 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 - } + cell.accessoryType = .none + cell.isUserInteractionEnabled = true + cell.textLabel?.isEnabled = true + cell.detailTextLabel?.isEnabled = true + let kbState = Manager.shared.stateForKeyboard(withID: keyboardID) cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType) } @@ -165,7 +154,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega tableView.cellForRow(at: indexPath)?.isSelected = false if tableView.cellForRow(at: indexPath)?.accessoryType == .disclosureIndicator { let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" - showLanguageDetailView(title: title, languageIndex: indexPath.section) + showLanguageSettingsView(title: title, languageIndex: indexPath.section) return } @@ -173,31 +162,28 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega let keyboardIndex = 0; let keyboard = language.keyboards![keyboardIndex] - let state = Manager.shared.stateForKeyboard(withID: keyboard.id) - if state != .downloading { - isUpdate = state != .needsDownload - let alertController = UIAlertController(title: "\(language.name): \(keyboard.name)", - message: "Would you like to download this keyboard?", - 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(keyboardIndex)} )) - self.present(alertController, animated: true, completion: nil) - } + //TODO: launch real Language Settings controller here + let languageSettingsController = UIAlertController(title: "\(language.name): \(keyboard.name)", + message: "This should be Language Settings", + preferredStyle: UIAlertControllerStyle.alert) + languageSettingsController.addAction(UIAlertAction(title: "Cancel", + style: UIAlertActionStyle.cancel, + handler: nil)) +// languageSettingsController.addAction(UIAlertAction(title: "Download", +// style: UIAlertActionStyle.default, +// handler: {_ in self.downloadHandler(keyboardIndex)} )) + self.present(languageSettingsController, animated: true, completion: nil) } override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? "" - showLanguageDetailView(title: title, languageIndex: indexPath.section) + showLanguageSettingsView(title: title, languageIndex: indexPath.section) } - private func showLanguageDetailView(title: String, languageIndex: Int) { - let langDetailView = LanguageDetailViewController(language: languages[languageIndex]) - langDetailView.title = title - navigationController?.pushViewController(langDetailView, animated: true) + private func showLanguageSettingsView(title: String, languageIndex: Int) { + let langSettingsView = LanguageSettingsViewController(languages[languageIndex]) + langSettingsView.title = title + navigationController?.pushViewController(langSettingsView, animated: true) } func errorAcknowledgmentHandler(withAction action: UIAlertAction) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift new file mode 100644 index 0000000000..c9b7377a1e --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -0,0 +1,104 @@ +// +// LanguageSettingsViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 5/29/19. +// Copyright © 2019 SIL International. All rights reserved. +// + +import UIKit + +class LanguageSettingsViewController: UITableViewController { + let language: Language + + public init(_ inLanguage: Language) { + language = inLanguage + super.init(nibName: nil, bundle: nil) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + // Uncomment the following line to preserve selection between presentations + // self.clearsSelectionOnViewWillAppear = false + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if 0 == section { + // so far as I know, there is always at least one keyboard or else we won't show + let kbct = language.keyboards!.count + return kbct + } else { + return 3 + } + } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // 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. + } + */ + +} diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.xib b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.xib new file mode 100644 index 0000000000..915de2b722 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.xib @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index fb15dd3211..96aa942fa0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -156,6 +156,7 @@ open class SettingsViewController: UITableViewController { @objc func bannerSwitchValueChanged(_ sender: Any) { let userData = Storage.active.userDefaults if let toggle = sender as? UISwitch { + // actually this should call into KMW, which controls the banner userData.set(toggle.isOn, forKey: "ShouldShowBanner") //??? userData.synchronize() } @@ -264,7 +265,14 @@ open class SettingsViewController: UITableViewController { var keyboardLanguages = [String: Language]() for k in userKeyboards { let l = k.languageID - keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)], lexicalModels: nil, font: nil, oskFont: nil) + var kbds: [Keyboard] + if let existingLanguage = keyboardLanguages[l] { + kbds = existingLanguage.keyboards ?? [] + kbds.append(Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)) + } else { + kbds = [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)] + } + keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: nil, font: nil, oskFont: nil) } // there shouldn't be any lexical models for languages that don't have a keyboard installed // but check @@ -279,7 +287,7 @@ open class SettingsViewController: UITableViewController { } userLanguages = keyboardLanguages - itemsArray[0]["subtitle"] = String(userLanguages.count) + itemsArray[0]["subtitle"] = "\(userLanguages.count) languages installed" } public func setIsDoneButtonEnabled(_ nc: UINavigationController, _ value: Bool) { From 43730d25019f2d0758b42f1f148c26dd39b6c844 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 30 May 2019 21:08:44 -0500 Subject: [PATCH 17/47] UI Language Settings shows keyboards and 3 settings launches KeyboardInfoView --- .../InstalledLanguagesViewController.swift | 2 +- .../LanguageSettingsViewController.swift | 122 ++++++++++++------ .../Settings/SettingsViewController.swift | 1 - 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index dc6395f47f..917e461117 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -21,8 +21,8 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega private var indices: [Int] = [] private var selectedSection = 0 private var isUpdate = false - private var languages: [Language] = [] private var installedLanguages: [String: Language] + private var languages: [Language] = [] private let keyboardRepository: KeyboardRepository? private let lexicalModelRepository: LexicalModelRepository? diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index c9b7377a1e..41317d186a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -10,7 +10,9 @@ import UIKit class LanguageSettingsViewController: UITableViewController { let language: Language - + private var userKeyboards: [String: Language] = [:] + private var settingsArray = [[String: String]]() + public init(_ inLanguage: Language) { language = inLanguage super.init(nibName: nil, bundle: nil) @@ -21,14 +23,15 @@ class LanguageSettingsViewController: UITableViewController { } override func viewDidLoad() { - super.viewDidLoad() + super.viewDidLoad() + title = "\(language.name) Settings" // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem - } + } // MARK: - Table view data source @@ -46,51 +49,86 @@ class LanguageSettingsViewController: UITableViewController { } } - /* override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - + let cellIdentifier = "LanguageSettingsCell" + + let reusableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) + if let cell = reusableCell { return cell + } + + let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) + cell.selectionStyle = .none + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 95.0 / 255.0, green: 196.0 / 255.0, blue: 217.0 / 255.0, alpha: 1.0) + cell.selectedBackgroundView = selectionColor + cell.textLabel?.font = cell.textLabel?.font?.withSize(12.0) + return cell } - */ - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + + cell.isUserInteractionEnabled = true + cell.textLabel?.isEnabled = true + cell.detailTextLabel?.isEnabled = true + if 0 == indexPath.section { // keyboard list + guard let keyboard = language.keyboards?[safe: indexPath.row] else { + return + } + cell.textLabel?.text = keyboard.name + cell.detailTextLabel?.text = keyboard.version + " " + keyboard.id + + cell.accessoryType = .disclosureIndicator + } else { // language settings + cell.accessoryType = .none + switch indexPath.row { + case 0: + cell.textLabel?.text = "Enable corrections" + case 1: + cell.textLabel?.text = "Enable predictions" + case 2: + cell.textLabel?.text = "Model" + cell.accessoryType = .disclosureIndicator + cell.detailTextLabel?.text = language.lexicalModels?[safe: 0]?.name + + default: + cell.textLabel?.text = "error" + } } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } + } + + override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.cellForRow(at: indexPath)?.isSelected = false + performAction(for: indexPath) + } + + override open func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { + performAction(for: indexPath) + } + + private func performAction(for indexPath: IndexPath) { + switch indexPath.section { + case 0: + showKeyboardInfoView(kb: (language.keyboards?[safe: indexPath.row])!) + default: + break } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - + } + + func showKeyboardInfoView(kb: Keyboard) { + let version = kb.version + + let infoView = KeyboardInfoViewController() + infoView.title = kb.name + infoView.keyboardCount = userKeyboards.count +// infoView.keyboardIndex = index + infoView.keyboardID = kb.id +// infoView.languageID = kb.languageID + infoView.keyboardVersion = version +// infoView.isCustomKeyboard = kb.isCustom + navigationController?.pushViewController(infoView, animated: true) + } + /* // MARK: - Navigation diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 96aa942fa0..1c23701a9c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -176,7 +176,6 @@ open class SettingsViewController: UITableViewController { } override open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - cell.selectionStyle = .none cell.accessoryType = .none cell.textLabel?.text = itemsArray[indexPath.row]["title"] cell.detailTextLabel?.text = itemsArray[indexPath.row]["subtitle"] From dd11a3622b6ea8a375c3e3040a38400553a9b6f7 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 3 Jun 2019 02:10:00 -0500 Subject: [PATCH 18/47] added switches to corrections and predictions cells --- .../LanguageSettingsViewController.swift | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 41317d186a..f4fabaee4b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -48,9 +48,24 @@ class LanguageSettingsViewController: UITableViewController { return 3 } } + + public func frameAtRightOfCell(cell cellFrame: CGRect, controlSize: CGSize) -> CGRect { + let rightOffset = cellFrame.size.width + let switchWidth: CGFloat = 20 + let switchX = rightOffset - switchWidth + let switchHeight = controlSize.height + let cellSwitchHeightDiff = cellFrame.size.height - switchHeight + let switchY = cellFrame.origin.y + 0.5 * cellSwitchHeightDiff + + let switchFrame = CGRect(x: switchX, + y: switchY, + width: switchWidth, + height: cellFrame.size.height) + return switchFrame + } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cellIdentifier = "LanguageSettingsCell" + let cellIdentifier = 0 == indexPath.section ? "KeyboardInLanguageSettingsCell" : "LanguageSettingsCell" let reusableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) if let cell = reusableCell { @@ -58,7 +73,28 @@ class LanguageSettingsViewController: UITableViewController { } let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) - cell.selectionStyle = .none + if 1 == indexPath.section { + //TODO: find the settings these are to show + if 0 == indexPath.row { + cell.accessoryType = .none + let doPredictionsSwitch = UISwitch() + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doPredictionsSwitch.frame.size) + doPredictionsSwitch.frame = switchFrame + doPredictionsSwitch.isOn = false + // showBannerSwitch.addTarget(self, action: #selector(self.predictionSwitchValueChanged), for: .valueChanged) + cell.addSubview(doPredictionsSwitch) + } else if 1 == indexPath.row { + cell.accessoryType = .none + let doCorrectionsSwitch = UISwitch() + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doCorrectionsSwitch.frame.size) + doCorrectionsSwitch.frame = switchFrame + doCorrectionsSwitch.isOn = false + // showBannerSwitch.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged) + cell.addSubview(doCorrectionsSwitch) + } else { // row 3 + cell.accessoryType = .disclosureIndicator + } + } let selectionColor = UIView() selectionColor.backgroundColor = UIColor(red: 95.0 / 255.0, green: 196.0 / 255.0, blue: 217.0 / 255.0, alpha: 1.0) cell.selectedBackgroundView = selectionColor From 7e3e6adfb832d4c9bd15bccae27614f0dd900783 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 5 Jun 2019 17:20:30 -0500 Subject: [PATCH 19/47] fix warnings by removing unnecessary 'public' declarations --- .../Extension/UserDefaults+Types.swift | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift index 5108379569..0b57fd3494 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift @@ -9,7 +9,7 @@ import Foundation public extension UserDefaults { - public func installableKeyboards(forKey key: String) -> [InstallableKeyboard]? { + func installableKeyboards(forKey key: String) -> [InstallableKeyboard]? { guard let array = array(forKey: key) as? [Data] else { return nil } @@ -22,7 +22,7 @@ public extension UserDefaults { } } - public func installableLexicalModels(forKey key: String) -> [InstallableLexicalModel]? { + func installableLexicalModels(forKey key: String) -> [InstallableLexicalModel]? { guard let array = array(forKey: key) as? [Data] else { return nil } @@ -35,7 +35,7 @@ public extension UserDefaults { } } - public func set(_ keyboards: [InstallableKeyboard]?, forKey key: String) { + func set(_ keyboards: [InstallableKeyboard]?, forKey key: String) { guard let keyboards = keyboards else { removeObject(forKey: key) return @@ -49,7 +49,7 @@ public extension UserDefaults { } } - public func set(_ lexicalModels: [InstallableLexicalModel]?, forKey key: String) { + func set(_ lexicalModels: [InstallableLexicalModel]?, forKey key: String) { guard let lexicalModels = lexicalModels else { removeObject(forKey: key) return @@ -63,7 +63,7 @@ public extension UserDefaults { } } - public func fullKeyboardID(forKey key: String) -> FullKeyboardID? { + func fullKeyboardID(forKey key: String) -> FullKeyboardID? { guard let data = data(forKey: key) else { return nil } @@ -75,7 +75,7 @@ public extension UserDefaults { } } - public func set(_ fullKeyboardID: FullKeyboardID?, forKey key: String) { + func set(_ fullKeyboardID: FullKeyboardID?, forKey key: String) { guard let id = fullKeyboardID else { removeObject(forKey: key) return @@ -88,7 +88,7 @@ public extension UserDefaults { } } - public func fullLexicalModelID(forKey key: String) -> FullLexicalModelID? { + func fullLexicalModelID(forKey key: String) -> FullLexicalModelID? { guard let data = data(forKey: key) else { return nil } @@ -100,7 +100,7 @@ public extension UserDefaults { } } - public func set(_ fullLexicalModelID: FullLexicalModelID?, forKey key: String) { + func set(_ fullLexicalModelID: FullLexicalModelID?, forKey key: String) { guard let id = fullLexicalModelID else { removeObject(forKey: key) return @@ -113,7 +113,7 @@ public extension UserDefaults { } } - public var userKeyboards: [InstallableKeyboard]? { + var userKeyboards: [InstallableKeyboard]? { get { return installableKeyboards(forKey: Key.userKeyboardsList) } @@ -123,7 +123,7 @@ public extension UserDefaults { } } - public var userLexicalModels: [InstallableLexicalModel]? { + var userLexicalModels: [InstallableLexicalModel]? { get { return installableLexicalModels(forKey: Key.userLexicalModelsList) } @@ -133,7 +133,7 @@ public extension UserDefaults { } } - public func userLexicalModels(forLanguage lgCode: String) -> [InstallableLexicalModel]? { + func userLexicalModels(forLanguage lgCode: String) -> [InstallableLexicalModel]? { if let models = userLexicalModels { return models.compactMap { ($0.languageID == lgCode) ? $0 : nil } } else { @@ -141,7 +141,7 @@ public extension UserDefaults { } } - public var currentKeyboardID: FullKeyboardID? { + var currentKeyboardID: FullKeyboardID? { get { return fullKeyboardID(forKey: Key.userCurrentKeyboard) } @@ -151,7 +151,7 @@ public extension UserDefaults { } } - public var currentLexicalModelID: FullLexicalModelID? { + var currentLexicalModelID: FullLexicalModelID? { get { return fullLexicalModelID(forKey: Key.userCurrentLexicalModel) } From 27f3736bee0be62a7734ebedb1bbbf83cbe93dc4 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 5 Jun 2019 17:41:31 -0500 Subject: [PATCH 20/47] define and use the constant InstallableConstants.defaultVersion instead of a magic string --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 2 +- .../Classes/Model/InstallableLexicalModel.swift | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 419d456551..e9d5a133f0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -1221,7 +1221,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat downloadQueue!.userInfo = commonUserData let request = HTTPDownloadRequest(url: lexicalModelURL, userInfo: commonUserData) - request.destinationFile = Storage.active.lexicalModelURL(forID: lexicalModel.id, version: lexicalModel.version ?? "0.1.0").path + request.destinationFile = Storage.active.lexicalModelURL(forID: lexicalModel.id, version: lexicalModel.version ?? InstallableConstants.defaultVersion).path request.tag = 0 downloadQueue!.addRequest(request) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Model/InstallableLexicalModel.swift b/ios/engine/KMEI/KeymanEngine/Classes/Model/InstallableLexicalModel.swift index 460340c8b9..6786d52d1f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Model/InstallableLexicalModel.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Model/InstallableLexicalModel.swift @@ -8,6 +8,10 @@ import Foundation +struct InstallableConstants { + static let defaultVersion = "0.1.0" +} + /// Mainly differs from the API `LexicalModel` by having an associated language. public struct InstallableLexicalModel: Codable { public var id: String @@ -38,7 +42,7 @@ public struct InstallableLexicalModel: Codable { self.name = lexicalModel.name self.languageID = languageID // self.languageName = language.name - self.version = lexicalModel.version ?? "0.1.0" + self.version = lexicalModel.version ?? InstallableConstants.defaultVersion self.isCustom = isCustom } } From 5dae098ead8e39daab7d7667ff80270b101dcc66 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 5 Jun 2019 18:06:55 -0500 Subject: [PATCH 21/47] fix compiler error by removing unnecessary 'public' declarations --- .../KeymanEngine/Classes/Extension/UserDefaults+Types.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift index 0b57fd3494..9127d7ffff 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift @@ -161,11 +161,11 @@ public extension UserDefaults { } } - public func userKeyboard(withFullID fullID: FullKeyboardID) -> InstallableKeyboard? { + func userKeyboard(withFullID fullID: FullKeyboardID) -> InstallableKeyboard? { return userKeyboards?.first { $0.fullID == fullID } } - public func userLexicalModel(withFullID fullID: FullLexicalModelID) -> InstallableLexicalModel? { + func userLexicalModel(withFullID fullID: FullLexicalModelID) -> InstallableLexicalModel? { return userLexicalModels?.first { $0.fullID == fullID } } From 564c437169c03dd7e78a8aa0e6d8794ad0bce2c9 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 6 Jun 2019 09:33:43 -0500 Subject: [PATCH 22/47] change the cell titles to match Android --- .../Classes/LanguagePicker/KeyboardInfoViewController.swift | 2 +- .../LanguagePicker/LexicalModelInfoViewController.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift index 31608594f8..bdd35d316c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift @@ -35,7 +35,7 @@ class KeyboardInfoViewController: UITableViewController, UIAlertViewDelegate { ]) } infoArray.append([ - "title": "Delete", + "title": "Uninstall keyboard", "subtitle": "" ]) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift index c6109e02e5..e19af5e6a3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift @@ -24,7 +24,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate infoArray = [[String: String]]() infoArray.append([ - "title": "Lexical model version", + "title": "Model version", "subtitle": lexicalModelVersion ]) @@ -35,7 +35,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate ]) } infoArray.append([ - "title": "Delete", + "title": "Uninstall model", "subtitle": "" ]) } From 337c3128dd5bb276287e4eb020f131faa12f24c7 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 6 Jun 2019 10:39:37 -0500 Subject: [PATCH 23/47] installed language settings show keyboards, two switches, and current model shows keyboard and model info deletes keyboard and model from their info views --- .../KeymanEngine.xcodeproj/project.pbxproj | 4 + .../KMEI/KeymanEngine/Classes/Manager.swift | 7 +- .../KeymanEngine/Classes/Model/Language.swift | 2 +- .../NotificationCenter+Typed.swift | 8 +- .../LanguageSettingsViewController.swift | 84 ++++++++++++++++--- .../Settings/SettingsViewController.swift | 16 +++- 6 files changed, 99 insertions(+), 22 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 6350512f4c..83e9089583 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -124,6 +124,8 @@ 9A9CB0862241717C00231FB9 /* LexicalModelRepositoryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */; }; 9AD4F53C229F85AC007992D3 /* LanguageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */; }; 9AD4F53D229F85AC007992D3 /* LanguageSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */; }; + 9AD4F53E22A8A25B007992D3 /* LanguageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */; }; + 9AD4F53F22A8A286007992D3 /* LanguageSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */; }; C007C4651F9F52D8006461B9 /* LanguagesAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = C007C4641F9F52D8006461B9 /* LanguagesAPICall.swift */; }; C024C9941FA6EB470060583B /* NotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9931FA6EB470060583B /* NotificationName.swift */; }; C024C9961FA6EC650060583B /* NotificationCenter+Typed.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9951FA6EC650060583B /* NotificationCenter+Typed.swift */; }; @@ -996,6 +998,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9AD4F53F22A8A286007992D3 /* LanguageSettingsViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1096,6 +1099,7 @@ 9A31E2C6224AE85000D9A491 /* UserDefaults+Types.swift in Sources */, 9A079E44223B61AE00581263 /* FullLexicalModelID.swift in Sources */, 9A079DE72236134B00581263 /* HTTPDownloader.swift in Sources */, + 9AD4F53E22A8A25B007992D3 /* LanguageSettingsViewController.swift in Sources */, 9A079E46223DBEF900581263 /* KeyPreviewView.swift in Sources */, 9A079DE32231A16600581263 /* Constants.swift in Sources */, 9A31E2C1224AAD8300D9A491 /* FontManager.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index e9d5a133f0..bd7c7e1599 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -120,10 +120,9 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat public var canRemoveLexicalModels = true /// Allow the default lexical model to be removed. - /// The last lexical model cannot be removed, so the default lexical model cannot be removed if it - /// is the only lexical model in the list, regardless of the value of this property. - /// The default value is false. - public var canRemoveDefaultLexicalModel = false + /// The last lexical model CAN be removed, as this is an optional feature + /// The default value is true. + public var canRemoveDefaultLexicalModel = true public let apiLexicalModelRepository: APILexicalModelRepository diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Model/Language.swift b/ios/engine/KMEI/KeymanEngine/Classes/Model/Language.swift index 8b4b80eaf3..d4b9c07446 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Model/Language.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Model/Language.swift @@ -19,7 +19,7 @@ public struct Language: Codable { /// Corresponding Keyboard objects. public let keyboards: [Keyboard]? - /// Corresponding Keyboard objects. + /// Corresponding lexical models. public let lexicalModels: [LexicalModel]? /// Font for input fields (and OSK if `oskFont` is not present). diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift b/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift index d0ef9651d5..e4e3980097 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift @@ -16,7 +16,7 @@ public extension NotificationCenter { /// - name: The typed name of the notification. /// - object: The object posting the notification. /// - value: Value to be sent to observer. - public func post(name: NotificationName, object: Any? = nil, value: T) { + func post(name: NotificationName, object: Any? = nil, value: T) { let userInfo = [userInfoKey: value] post(name: name.name, object: object, userInfo: userInfo) } @@ -33,7 +33,7 @@ public extension NotificationCenter { /// - Returns: Observer that automatically deregisters itself when deinitializing. /// - SeeAlso: `addObserver(name:object:queue:observer:function:)` to add an instance method with a weak reference to /// the object instance. - public func addObserver(forName name: NotificationName, + func addObserver(forName name: NotificationName, object: Any? = nil, queue: OperationQueue? = nil, using block: @escaping (T) -> Void) -> NotificationObserver { @@ -58,7 +58,7 @@ public extension NotificationCenter { /// - function: A static reference to the instance method that is to be called on `observer` /// (eg. `Class.instanceMethod`). /// - Returns: Observer that automatically deregisters itself when deinitializing. - public func addObserver(forName name: NotificationName, + func addObserver(forName name: NotificationName, object: Any? = nil, queue: OperationQueue? = nil, observer: O, @@ -82,7 +82,7 @@ public extension NotificationCenter { /// - function: A static reference to the instance method that is to be called on `observer` /// (eg. `Class.instanceMethod`). /// - Returns: Observer that automatically deregisters itself when deinitializing. - public func addObserver(forName name: NotificationName, + func addObserver(forName name: NotificationName, object: Any? = nil, queue: OperationQueue? = nil, observer: O, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index f4fabaee4b..fbdd2f9447 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -125,7 +125,11 @@ class LanguageSettingsViewController: UITableViewController { case 2: cell.textLabel?.text = "Model" cell.accessoryType = .disclosureIndicator - cell.detailTextLabel?.text = language.lexicalModels?[safe: 0]?.name + if let currentModel = language.lexicalModels?[safe: 0] { + cell.detailTextLabel?.text = currentModel.name + } else { + cell.detailTextLabel?.text = "" + } default: cell.textLabel?.text = "error" @@ -146,6 +150,13 @@ class LanguageSettingsViewController: UITableViewController { switch indexPath.section { case 0: showKeyboardInfoView(kb: (language.keyboards?[safe: indexPath.row])!) + case 1: + switch indexPath.row { + case 2: + showLexicalModelInfoView() + default: + break + } default: break } @@ -153,16 +164,67 @@ class LanguageSettingsViewController: UITableViewController { func showKeyboardInfoView(kb: Keyboard) { let version = kb.version - - let infoView = KeyboardInfoViewController() - infoView.title = kb.name - infoView.keyboardCount = userKeyboards.count -// infoView.keyboardIndex = index - infoView.keyboardID = kb.id -// infoView.languageID = kb.languageID - infoView.keyboardVersion = version -// infoView.isCustomKeyboard = kb.isCustom - navigationController?.pushViewController(infoView, animated: true) + let matchingFullID = FullKeyboardID(keyboardID: kb.id, languageID: language.id) + + let userData = Storage.active.userDefaults + + // If user defaults for keyboards list does not exist, do nothing. + guard var globalUserKeyboards = userData.userKeyboards else { + log.error("no keyboards in the global keyboards list!") + return + } + + if let index = globalUserKeyboards.index(where: { $0.fullID == matchingFullID }) { + guard index < globalUserKeyboards.count else { + return + } + let kbIndex:Int = index + let thisKb = globalUserKeyboards[kbIndex] + let infoView = KeyboardInfoViewController() + infoView.title = thisKb.name + infoView.keyboardCount = userKeyboards.count + infoView.keyboardIndex = index + infoView.keyboardID = thisKb.id + infoView.languageID = language.id + infoView.keyboardVersion = version + infoView.isCustomKeyboard = thisKb.isCustom + navigationController?.pushViewController(infoView, animated: true) + } else { + log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!") + return + } + } + + 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.index(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!") + } + } } /* diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 1c23701a9c..5b76b1fc66 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -252,8 +252,17 @@ open class SettingsViewController: UITableViewController { log.info("prepare for segue") // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. + } + + // MARK: - language access - + private func installed2API(_ installedList: [InstallableLexicalModel]) -> [LexicalModel] { + var returnList = [LexicalModel]() + for ilm in installedList { + returnList.append(LexicalModel(id: ilm.id, name: ilm.name, license: "", version: ilm.version, languages: [], authorName: "", fileSize: 0, filename: "no filename", sourcePath: nil, authorEmail: nil, description: nil, packageFileSize: 0, packageFilename: "", packageIncludes: nil, isDefault: false, lastModified: nil, minKeymanVersion: nil)) } - + return returnList + } + // MARK: - language access - private func loadUserLanguages() { //iterate the list of installed languages and save their names @@ -271,7 +280,10 @@ open class SettingsViewController: UITableViewController { } else { kbds = [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)] } - keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: nil, font: nil, oskFont: nil) + let userDefaults = Storage.active.userDefaults + let lmListInstalled: [InstallableLexicalModel] = userDefaults.userLexicalModels(forLanguage: k.languageID) ?? [] + let lmList = installed2API(lmListInstalled) + keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: lmList, font: nil, oskFont: nil) } // there shouldn't be any lexical models for languages that don't have a keyboard installed // but check From 7fa69b2d5df41d6519eb42cdf63fd5c63111b89f Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 6 Jun 2019 21:21:55 -0500 Subject: [PATCH 24/47] we should always be able to remove a lexical model LexicalModelPickerViewController can be used to view all LMs or just those for a language In language settings, don't assume the current model is relevant to this language launch the LexicalModelsView from language settings, not the LM Info view directly remember the user's chosen currentLexicalModelID if no preference, register the first one --- .../LexicalModelPickerViewController.swift | 38 +++++++++++++------ .../KMEI/KeymanEngine/Classes/Manager.swift | 30 +++++++++++---- .../LanguageSettingsViewController.swift | 20 ++++++++-- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 749e6e69ef..28e2a0ba80 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -15,6 +15,7 @@ private let toolbarActivityIndicatorTag = 102 class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelegate { private var userLexicalModels: [InstallableLexicalModel] = [InstallableLexicalModel]() + public var language: Language? = nil private var updateQueue: [InstallableLexicalModel]? private var _isDoneButtonEnabled = false private var isDidUpdateCheck = false @@ -112,13 +113,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega return false } - if !Manager.shared.canRemoveDefaultLexicalModel { - return indexPath.row != 0 - } - if indexPath.row > 0 { - return true - } - return userLexicalModels.count > 1 + return true } override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, @@ -127,7 +122,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega return } - if Manager.shared.removeLexicalModel(at: indexPath.row) { + let globalIndex = local2globalIndex(indexPath.row) + if Manager.shared.removeLexicalModel(at: globalIndex) { loadUserLexicalModels() } setIsDoneButtonEnabled(true) @@ -146,7 +142,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega let infoView = LexicalModelInfoViewController() infoView.title = lm.name infoView.lexicalModelCount = userLexicalModels.count - infoView.lexicalModelIndex = index + infoView.lexicalModelIndex = local2globalIndex(index) infoView.lexicalModelID = lm.id infoView.languageID = lm.languageID infoView.lexicalModelVersion = version @@ -167,7 +163,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega if Manager.shared.currentLexicalModelID == lm.fullID { cell.selectionStyle = .blue cell.isSelected = true - cell.accessoryType = .detailDisclosureButton + cell.accessoryType = .checkmark } else { cell.selectionStyle = .none cell.isSelected = false @@ -179,6 +175,19 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega switchLexicalModel(indexPath.row) } + func local2globalIndex(_ localIndex: Int) -> Int { + let globalIndex: Int + if let lang = self.language { + let lm = userLexicalModels[localIndex] + globalIndex = Storage.active.userDefaults.userLexicalModels?.firstIndex(where: { + $0.languageID == lang.id && $0.id == lm.id + }) ?? 0 + } else { + globalIndex = localIndex + } + return globalIndex + } + private func lexicalModelDownloadStarted(_ lexicalModels: [InstallableLexicalModel]) { view.isUserInteractionEnabled = false navigationItem.leftBarButtonItem?.isEnabled = false @@ -275,8 +284,15 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } } + // if we have a language set, filter models down to those of that language private func loadUserLexicalModels() { - userLexicalModels = Storage.active.userDefaults.userLexicalModels ?? [] + if let lang = self.language { + userLexicalModels = Storage.active.userDefaults.userLexicalModels?.filter({ + $0.languageID == lang.id + }) ?? [] + } else { + userLexicalModels = Storage.active.userDefaults.userLexicalModels ?? [] + } tableView.reloadData() } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index bd7c7e1599..a6ba641e1f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -134,7 +134,24 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat public var openURL: ((URL) -> Bool)? var currentKeyboardID: FullKeyboardID? - var currentLexicalModelID: FullLexicalModelID? + private var _currentLexicalModelID: FullLexicalModelID? + var currentLexicalModelID: FullLexicalModelID? { + get { + if _currentLexicalModelID == nil { + let userData = Util.isSystemKeyboard ? UserDefaults.standard : Storage.active.userDefaults + _currentLexicalModelID = userData.currentLexicalModelID + } + return _currentLexicalModelID + } + + set(value) { + _currentLexicalModelID = value + let userData = Util.isSystemKeyboard ? UserDefaults.standard : Storage.active.userDefaults + userData.currentLexicalModelID = _currentLexicalModelID + userData.synchronize() + } + } + var currentRequest: HTTPDownloadRequest? var shouldReloadKeyboard = false var shouldReloadLexicalModel = false @@ -280,8 +297,11 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // If we have a lexical model for the keyboard's language, activate it. if let valid_models = Storage.active.userDefaults.userLexicalModels(forLanguage: kb.languageID) { if valid_models.count > 0 { - //let lm = Storage.active.userDefaults.userLexicalModel(withFullID: valid_models[0].fullID)! - _ = Manager.shared.registerLexicalModel(valid_models[0]) + if let lm = self.currentLexicalModel { + _ = Manager.shared.registerLexicalModel(lm) + } else { + _ = Manager.shared.registerLexicalModel(valid_models[0]) + } } } @@ -338,10 +358,6 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat inputViewController.registerLexicalModel(lm) - let userData = Util.isSystemKeyboard ? UserDefaults.standard : Storage.active.userDefaults - userData.currentLexicalModelID = lm.fullID - userData.synchronize() - if isKeymanHelpOn { inputViewController.showHelpBubble(afterDelay: 1.5) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index fbdd2f9447..cf9a9c2420 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -125,8 +125,15 @@ class LanguageSettingsViewController: UITableViewController { case 2: cell.textLabel?.text = "Model" cell.accessoryType = .disclosureIndicator - if let currentModel = language.lexicalModels?[safe: 0] { - cell.detailTextLabel?.text = currentModel.name + if let currentModel = Manager.shared.currentLexicalModel { +// if language.id == currentModel.id { + if language.lexicalModels?.contains(where: { + $0.id == currentModel.id + }) ?? false { + cell.detailTextLabel?.text = currentModel.name + } else { + cell.detailTextLabel?.text = "" + } } else { cell.detailTextLabel?.text = "" } @@ -153,7 +160,7 @@ class LanguageSettingsViewController: UITableViewController { case 1: switch indexPath.row { case 2: - showLexicalModelInfoView() + showLexicalModelsView() default: break } @@ -195,6 +202,13 @@ class LanguageSettingsViewController: UITableViewController { } } + func showLexicalModelsView() { + //LanguageLexicalModelPickerViewController? (should show just the models for this language) + let lmListView = LexicalModelPickerViewController() + lmListView.language = self.language + navigationController?.pushViewController(lmListView, animated: true) + } + func showLexicalModelInfoView() { if let lm = language.lexicalModels?[safe: 0] { let version = lm.version From 253e2ab1e6c9ea8a90e57ed104a43120f3f18972 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 10 Jun 2019 22:11:08 -0500 Subject: [PATCH 25/47] rename kb to lm in function copied from a keyboard function --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index a6ba641e1f..a4d045cd69 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -481,21 +481,21 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat return false } - let kb = userLexicalModels[index] + let lm = userLexicalModels[index] userLexicalModels.remove(at: index) userData.userLexicalModels = userLexicalModels userData.set([Date()], forKey: Key.synchronizeSWLexicalModel) userData.synchronize() - log.info("Removing lexical model with ID \(kb.id) and languageID \(kb.languageID)") + log.info("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID)") // Set a new lexical model if deleting the current one - if kb.fullID == currentLexicalModelID { + if lm.fullID == currentLexicalModelID { _ = registerLexicalModel(userLexicalModels[0]) } - if !userLexicalModels.contains(where: { $0.id == kb.id }) { - let lexicalModelDir = Storage.active.lexicalModelDir(forID: kb.id) + if !userLexicalModels.contains(where: { $0.id == lm.id }) { + let lexicalModelDir = Storage.active.lexicalModelDir(forID: lm.id) FontManager.shared.unregisterFonts(in: lexicalModelDir, fromSystemOnly: false) log.info("Deleting directory \(lexicalModelDir)") if (try? FileManager.default.removeItem(at: lexicalModelDir)) == nil { @@ -505,7 +505,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat log.info("User has another language installed. Skipping delete of lexical model files.") } - NotificationCenter.default.post(name: Notifications.lexicalModelRemoved, object: self, value: kb) + NotificationCenter.default.post(name: Notifications.lexicalModelRemoved, object: self, value: lm) return true } From 267968fbe8d7228aeb61fa891d656c4ff55f6334 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 12 Jun 2019 20:55:05 -0500 Subject: [PATCH 26/47] add Manage dictionary line to Language Settings view --- .../LanguageSettingsViewController.swift | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index cf9a9c2420..0923c4f03b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -33,6 +33,15 @@ class LanguageSettingsViewController: UITableViewController { // self.navigationItem.rightBarButtonItem = self.editButtonItem } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) +// navigationController?.setToolbarHidden(true, animated: true) + // if no rows to show yet, show a loading indicator + log.info("didAppear: LanguageSettingsViewController") + } + + // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { @@ -45,7 +54,7 @@ class LanguageSettingsViewController: UITableViewController { let kbct = language.keyboards!.count return kbct } else { - return 3 + return 4 } } @@ -91,7 +100,7 @@ class LanguageSettingsViewController: UITableViewController { doCorrectionsSwitch.isOn = false // showBannerSwitch.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged) cell.addSubview(doCorrectionsSwitch) - } else { // row 3 + } else { // rows 3 and 4 cell.accessoryType = .disclosureIndicator } } @@ -125,18 +134,22 @@ class LanguageSettingsViewController: UITableViewController { case 2: cell.textLabel?.text = "Model" cell.accessoryType = .disclosureIndicator - if let currentModel = Manager.shared.currentLexicalModel { -// if language.id == currentModel.id { - if language.lexicalModels?.contains(where: { - $0.id == currentModel.id - }) ?? false { - cell.detailTextLabel?.text = currentModel.name - } else { - cell.detailTextLabel?.text = "" + if let modelCt = language.lexicalModels?.count { + switch modelCt { + case 0: + cell.detailTextLabel?.text = "no models installed" + case 1: + cell.detailTextLabel?.text = "one model installed" + default: + cell.detailTextLabel?.text = "\(modelCt) models installed" } } else { - cell.detailTextLabel?.text = "" + cell.detailTextLabel?.text = "no models installed" } + case 3: + cell.textLabel?.text = "Manage dictionary" + cell.accessoryType = .disclosureIndicator + cell.isUserInteractionEnabled = false default: cell.textLabel?.text = "error" From 477cb9b55d83e5ff0f6467177bdf0f50e8d2805d Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 12 Jun 2019 20:56:21 -0500 Subject: [PATCH 27/47] don't put in Done button when launched from Language Settings so the Back button works --- .../LexicalModelPickerViewController.swift | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 28e2a0ba80..92eae44db8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -27,8 +27,14 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega override func viewDidLoad() { super.viewDidLoad() - title = "LexicalModels" - setIsDoneButtonEnabled(false) + if let langName = language?.name { + title = "\(langName) Lexical Models" + } else { + title = "Lexical Models" + } + if language == nil { + setIsDoneButtonEnabled(false) + } isDidUpdateCheck = false updateQueue = nil if Manager.shared.canAddNewLexicalModels { @@ -63,6 +69,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + log.info("didAppear: LexicalModelPickerViewController") if isDidUpdateCheck || !checkUpdates() { return } @@ -84,7 +91,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega navigationController?.setToolbarHidden(false, animated: true) scroll(toSelectedLexicalModel: false) - } +} override func numberOfSections(in tableView: UITableView) -> Int { return 1 @@ -135,7 +142,9 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } func showLexicalModelInfoView(with index: Int) { - setIsDoneButtonEnabled(true) + if language == nil { + setIsDoneButtonEnabled(true) + } let lm = userLexicalModels[index] let version = lm.version @@ -407,7 +416,9 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega button?.isEnabled = false let vc = LanguageViewController(Manager.shared.apiLexicalModelRepository) //may need to be different for models navigationController?.pushViewController(vc, animated: true) - setIsDoneButtonEnabled(true) + if language == nil { + setIsDoneButtonEnabled(true) + } } } From e9b2ce33dbdee606cb805eab036c87e7e865c129 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 12 Jun 2019 20:57:22 -0500 Subject: [PATCH 28/47] add info debugging to track which viewcontroller is appearing --- .../KeyboardInfoViewController.swift | 3 ++- .../KeyboardPickerViewController.swift | 1 + .../LanguageDetailViewController.swift | 6 ++++++ .../LanguageViewController.swift | 5 ++++- .../LexicalModelInfoViewController.swift | 1 + .../InstalledLanguagesViewController.swift | 20 +++++++++++++++++++ .../Settings/SettingsViewController.swift | 3 ++- 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift index bdd35d316c..d6c93e5820 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardInfoViewController.swift @@ -43,7 +43,8 @@ class KeyboardInfoViewController: UITableViewController, UIAlertViewDelegate { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.setToolbarHidden(true, animated: true) - } + log.info("didAppear: KeyboardInfoViewController") +} override func numberOfSections(in tableView: UITableView) -> Int { return 1 diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift index 3f9ac7ffb8..327f9c5d78 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift @@ -85,6 +85,7 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { navigationController?.setToolbarHidden(false, animated: true) scroll(toSelectedKeyboard: false) + log.info("didAppear: KeyboardPickerViewController") } override func numberOfSections(in tableView: UITableView) -> Int { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift index d4832dc0c7..cafe877470 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift @@ -45,6 +45,12 @@ class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate { observer: self, function: LanguageDetailViewController.keyboardDownloadFailed) } + + override open func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + log.info("didAppear: LanguageDetailViewController (actually willAppear)") + } override func numberOfSections(in tableView: UITableView) -> Int { return language.keyboards!.count diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift index 44d0c28611..8ec8141ab2 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift @@ -54,6 +54,7 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { } loadUserKeyboards() + loadUserLexicalModels() } override func viewDidLoad() { @@ -76,8 +77,10 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { // if no rows to show yet, show a loading indicator if numberOfSections(in: tableView) == 0 { showActivityView() + } else { + log.info("didAppear: LanguageViewController") } - } + } override func numberOfSections(in tableView: UITableView) -> Int { return languages.count diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift index e19af5e6a3..67e50e5a1a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelInfoViewController.swift @@ -43,6 +43,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.setToolbarHidden(true, animated: true) + log.info("didAppear: LexicalModelInfoViewController") } override func numberOfSections(in tableView: UITableView) -> Int { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 917e461117..af16ccbbf4 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -59,6 +59,13 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega forName: Notifications.keyboardDownloadFailed, observer: self, function: InstalledLanguagesViewController.keyboardDownloadFailed) + + if Manager.shared.canAddNewKeyboards { + let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, + action: #selector(self.addClicked)) + navigationItem.rightBarButtonItem = addButton + } + } override func viewDidAppear(_ animated: Bool) { @@ -67,6 +74,8 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega // if no rows to show yet, show a loading indicator if numberOfSections(in: tableView) == 0 { showActivityView() + } else { + log.info("didAppear: InstalledLanguagesViewController") } } @@ -348,5 +357,16 @@ extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { dismissActivityView() showConnectionErrorAlert() } + + @objc func addClicked(_ sender: Any) { + showAddKeyboard() + } + + func showAddKeyboard() { + let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) + button?.isEnabled = false + let vc = LanguageViewController(Manager.shared.apiKeyboardRepository) + navigationController?.pushViewController(vc, animated: true) + } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 5b76b1fc66..d8c96dcee5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -16,7 +16,8 @@ open class SettingsViewController: UITableViewController { super.viewWillAppear(animated) loadUserLanguages() - } + log.info("didAppear: SettingsViewController (actually willAppear)") + } override open func viewDidLoad() { super.viewDidLoad() From 9fe84b4b56beb5469e2317ba6717720d7c5dbeae Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 12 Jun 2019 21:08:40 -0500 Subject: [PATCH 29/47] call loadUserKeyboards when appearing, not just when loading prevents error showing old list when all keyboards of a language were deleted --- .../Settings/InstalledLanguagesViewController.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index af16ccbbf4..730e5aaa70 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -44,7 +44,6 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega override func loadView() { super.loadView() languages = languageList(installedLanguages) - loadUserKeyboards() } override func viewDidLoad() { @@ -65,7 +64,11 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega action: #selector(self.addClicked)) navigationItem.rightBarButtonItem = addButton } - + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadUserKeyboards() } override func viewDidAppear(_ animated: Bool) { @@ -74,9 +77,8 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega // if no rows to show yet, show a loading indicator if numberOfSections(in: tableView) == 0 { showActivityView() - } else { - log.info("didAppear: InstalledLanguagesViewController") } + log.info("didAppear: InstalledLanguagesViewController") } override func numberOfSections(in tableView: UITableView) -> Int { From be5b47cbe1c42e9c29c1c11c24cbf3735ff4fd13 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 17 Jun 2019 19:36:23 -0500 Subject: [PATCH 30/47] remove Manage Dictionary until we implement that --- .../Classes/Settings/LanguageSettingsViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 0923c4f03b..3f7e82ddd4 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -54,7 +54,7 @@ class LanguageSettingsViewController: UITableViewController { let kbct = language.keyboards!.count return kbct } else { - return 4 + return 3 // 4 in future when we can manage a user dictionary } } @@ -146,7 +146,7 @@ class LanguageSettingsViewController: UITableViewController { } else { cell.detailTextLabel?.text = "no models installed" } - case 3: + case 3: // future cell.textLabel?.text = "Manage dictionary" cell.accessoryType = .disclosureIndicator cell.isUserInteractionEnabled = false From 3d28463abda437bf9026f740fc6955d340ed8386 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 17 Jun 2019 19:46:27 -0500 Subject: [PATCH 31/47] use 16.0pt font to be like the Languages list and others --- .../Classes/Settings/LanguageSettingsViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 3f7e82ddd4..adb4d38b50 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -107,7 +107,7 @@ class LanguageSettingsViewController: UITableViewController { let selectionColor = UIView() selectionColor.backgroundColor = UIColor(red: 95.0 / 255.0, green: 196.0 / 255.0, blue: 217.0 / 255.0, alpha: 1.0) cell.selectedBackgroundView = selectionColor - cell.textLabel?.font = cell.textLabel?.font?.withSize(12.0) + cell.textLabel?.font = cell.textLabel?.font?.withSize(16.0) return cell } From 6bab22435f7e597f3583606d01b8977ef60b4636 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 17 Jun 2019 19:47:08 -0500 Subject: [PATCH 32/47] title Installed Languages instead of Languages Settings --- .../Classes/Settings/InstalledLanguagesViewController.swift | 2 +- .../KeymanEngine/Classes/Settings/SettingsViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 730e5aaa70..1c60031ed1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -48,7 +48,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega override func viewDidLoad() { super.viewDidLoad() - title = "Languages Settings" + title = "Installed Languages" selectedSection = NSNotFound keyboardDownloadStartedObserver = NotificationCenter.default.addObserver( forName: Notifications.keyboardDownloadStarted, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index d8c96dcee5..e9bfb558f1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -54,7 +54,7 @@ open class SettingsViewController: UITableViewController { itemsArray = [[String: String]]() itemsArray.append([ - "title": "Languages Settings", + "title": "Installed Languages", "subtitle": "0", //count of installed languages as string "reuseid" : "language" ]) From 34aa3b806a5c38dfff472aa45a734a16a7c7f94f Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 17 Jun 2019 21:13:27 -0500 Subject: [PATCH 33/47] headers for keyboards and language settings in Language Settings UI --- .../LanguageSettingsViewController.swift | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index adb4d38b50..4282540c7a 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -22,6 +22,11 @@ class LanguageSettingsViewController: UITableViewController { fatalError("init(coder:) has not been implemented") } + open override func loadView() { + super.loadView() + tableView?.delegate = self + } + override func viewDidLoad() { super.viewDidLoad() title = "\(language.name) Settings" @@ -42,7 +47,7 @@ class LanguageSettingsViewController: UITableViewController { } - // MARK: - Table view data source + // MARK: - Table view data source UITableViewDataSource override func numberOfSections(in tableView: UITableView) -> Int { return 2 @@ -110,6 +115,38 @@ class LanguageSettingsViewController: UITableViewController { cell.textLabel?.font = cell.textLabel?.font?.withSize(16.0) return cell } + + // MARK: - UITableViewDelegate + + // fixed font style. use custom view (UILabel) if you want something different + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + var title: String + switch (section) { + case 0: + title = "keyboards" + case 1: + title = "language settings" + default: + title = "unknown header" + } + return title + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat + { + if (section == 0 || section == 1) + { + return 32.0; + } + else + { + return CGFloat.leastNormalMagnitude; + } + } + + override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return CGFloat.leastNonzeroMagnitude + } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { From 91b9595f056a4b3cd16a9dac63face391d9ddb12 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 17 Jun 2019 21:25:15 -0500 Subject: [PATCH 34/47] put model name in display text, model id in detailed display text --- .../LanguagePicker/LexicalModelPickerViewController.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 92eae44db8..697bcdeb68 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -93,6 +93,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega scroll(toSelectedLexicalModel: false) } + // MARK: - Table view data source UITableViewDataSource + override func numberOfSections(in tableView: UITableView) -> Int { return 1 } @@ -159,14 +161,16 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega navigationController?.pushViewController(infoView, animated: true) } + // MARK: - UITableViewDelegate + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.selectionStyle = .none let lm = userLexicalModels[indexPath.row] - cell.textLabel?.text = lm.languageID // maybe do a lookup for lm.languageName - cell.detailTextLabel?.text = lm.name + cell.textLabel?.text = lm.name + cell.detailTextLabel?.text = lm.id // maybe put a longer description here? cell.tag = indexPath.row if Manager.shared.currentLexicalModelID == lm.fullID { From 04e2fde8d18573979b5cf6a9b6fd1d8832c07aec Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Tue, 18 Jun 2019 21:48:59 -0500 Subject: [PATCH 35/47] fixed indentation --- .../LanguageSettingsViewController.swift | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 4282540c7a..bb984a8207 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -78,43 +78,43 @@ class LanguageSettingsViewController: UITableViewController { return switchFrame } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cellIdentifier = 0 == indexPath.section ? "KeyboardInLanguageSettingsCell" : "LanguageSettingsCell" - - let reusableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) - if let cell = reusableCell { - return cell - } - - let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) - if 1 == indexPath.section { - //TODO: find the settings these are to show - if 0 == indexPath.row { - cell.accessoryType = .none - let doPredictionsSwitch = UISwitch() - let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doPredictionsSwitch.frame.size) - doPredictionsSwitch.frame = switchFrame - doPredictionsSwitch.isOn = false - // showBannerSwitch.addTarget(self, action: #selector(self.predictionSwitchValueChanged), for: .valueChanged) - cell.addSubview(doPredictionsSwitch) - } else if 1 == indexPath.row { - cell.accessoryType = .none - let doCorrectionsSwitch = UISwitch() - let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doCorrectionsSwitch.frame.size) - doCorrectionsSwitch.frame = switchFrame - doCorrectionsSwitch.isOn = false - // showBannerSwitch.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged) - cell.addSubview(doCorrectionsSwitch) - } else { // rows 3 and 4 - cell.accessoryType = .disclosureIndicator - } - } - let selectionColor = UIView() - selectionColor.backgroundColor = UIColor(red: 95.0 / 255.0, green: 196.0 / 255.0, blue: 217.0 / 255.0, alpha: 1.0) - cell.selectedBackgroundView = selectionColor - cell.textLabel?.font = cell.textLabel?.font?.withSize(16.0) + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellIdentifier = 0 == indexPath.section ? "KeyboardInLanguageSettingsCell" : "LanguageSettingsCell" + + let reusableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) + if let cell = reusableCell { return cell } + + let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) + if 1 == indexPath.section { + //TODO: find the settings these are to show + if 0 == indexPath.row { + cell.accessoryType = .none + let doPredictionsSwitch = UISwitch() + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doPredictionsSwitch.frame.size) + doPredictionsSwitch.frame = switchFrame + doPredictionsSwitch.isOn = false + // showBannerSwitch.addTarget(self, action: #selector(self.predictionSwitchValueChanged), for: .valueChanged) + cell.addSubview(doPredictionsSwitch) + } else if 1 == indexPath.row { + cell.accessoryType = .none + let doCorrectionsSwitch = UISwitch() + let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doCorrectionsSwitch.frame.size) + doCorrectionsSwitch.frame = switchFrame + doCorrectionsSwitch.isOn = false + // showBannerSwitch.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged) + cell.addSubview(doCorrectionsSwitch) + } else { // rows 3 and 4 + cell.accessoryType = .disclosureIndicator + } + } + let selectionColor = UIView() + selectionColor.backgroundColor = UIColor(red: 95.0 / 255.0, green: 196.0 / 255.0, blue: 217.0 / 255.0, alpha: 1.0) + cell.selectedBackgroundView = selectionColor + cell.textLabel?.font = cell.textLabel?.font?.withSize(16.0) + return cell + } // MARK: - UITableViewDelegate @@ -163,7 +163,7 @@ class LanguageSettingsViewController: UITableViewController { cell.accessoryType = .disclosureIndicator } else { // language settings cell.accessoryType = .none - switch indexPath.row { + switch indexPath.row { case 0: cell.textLabel?.text = "Enable corrections" case 1: @@ -211,8 +211,8 @@ class LanguageSettingsViewController: UITableViewController { switch indexPath.row { case 2: showLexicalModelsView() - default: - break + default: + break } default: break From 303ddf61b9a62853dfc051e594a48b3a666ae676 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 19 Jun 2019 21:40:07 -0500 Subject: [PATCH 36/47] made UI changes from main thread only with dispatch async --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index a4d045cd69..033717e36c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -370,6 +370,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat return true } + /// Adds a new lexical model to the list in the lexical model picker if it doesn't already exist. /// The lexical model must be downloaded (see `downloadLexicalModel()`) or preloaded (see `preloadLanguageFile()`) public func addLexicalModel(_ lexicalModel: InstallableLexicalModel) { @@ -393,7 +394,8 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat userDefaults.userLexicalModels = userLexicalModels userDefaults.set([Date()], forKey: Key.synchronizeSWLexicalModel) userDefaults.synchronize() - } + log.info("Added lexical model ID: \(lexicalModel.id) name: \(lexicalModel.name)") +} /// Removes a keyboard from the list in the keyboard picker if it exists. /// - Returns: The keyboard exists and was removed @@ -986,13 +988,22 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat do { try Manager.shared.parseLMKMP(kmp.sourceFolder) log.info("successfully installed the lexical model from: \(kmp.sourceFolder)") + DispatchQueue.main.async { + self.apiLexicalModelRepository.delegate?.lexicalModelRepositoryDidFetch(self.apiLexicalModelRepository) + } //this can fail gracefully and not show errors to users try FileManager.default.removeItem(at: downloadedPackageFile) } catch { log.error("Error installing the lexical model: \(error)") + DispatchQueue.main.async { + self.apiLexicalModelRepository.delegate?.lexicalModelRepository(self.apiLexicalModelRepository, didFailFetch: error) + } } } else { log.error("Error extracting the lexical model from the package: \(KMPError.invalidPackage)") + DispatchQueue.main.async { + self.apiLexicalModelRepository.delegate?.lexicalModelRepository(self.apiLexicalModelRepository, didFailFetch: APILexicalModelFetchError.noData) + } } }) } From e3a64f811afa8c7e23140032891a131c1e7a4c3d Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 26 Jun 2019 13:50:47 -0500 Subject: [PATCH 37/47] download lexical model after keyboard install via Installed Languages Plus button --- .../APILexicalModelRepository.swift | 2 +- .../LexicalModelRepositoryDelegate.swift | 2 +- .../KeyboardPickerViewController.swift | 74 ++++++++++++++++--- .../LanguageViewController.swift | 13 +++- .../LexicalModelPickerViewController.swift | 1 + .../KMEI/KeymanEngine/Classes/Manager.swift | 46 ++++++++---- .../Model/LexicalModelKeymanPackage.swift | 2 +- .../InstalledLanguagesViewController.swift | 4 +- .../LanguageSettingsViewController.swift | 24 +++--- .../Settings/SettingsViewController.swift | 2 +- 10 files changed, 125 insertions(+), 45 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/APILexicalModelRepository.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/APILexicalModelRepository.swift index 07330f265f..3a47d48a2b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/APILexicalModelRepository.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/APILexicalModelRepository.swift @@ -75,7 +75,7 @@ public class APILexicalModelRepository: LexicalModelRepository { log.info("Request list completed -- \(result.lexicalModels.count) lexical models.") DispatchQueue.main.async { - self.delegate?.lexicalModelRepositoryDidFetch(self) + self.delegate?.lexicalModelRepositoryDidFetchList(self) fetchCompletionHandler(lexicalModels, nil) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/LexicalModelRepositoryDelegate.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/LexicalModelRepositoryDelegate.swift index e9e2d5b2c5..a78750f182 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/LexicalModelRepositoryDelegate.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardRepository/LexicalModelRepositoryDelegate.swift @@ -9,6 +9,6 @@ import Foundation public protocol LexicalModelRepositoryDelegate: class { - func lexicalModelRepositoryDidFetch(_ repository: LexicalModelRepository) + func lexicalModelRepositoryDidFetchList(_ repository: LexicalModelRepository) func lexicalModelRepository(_ repository: LexicalModelRepository, didFailFetch error: Error) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift index 327f9c5d78..9e3f4a93fb 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift @@ -53,6 +53,20 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { forName: Notifications.keyboardDownloadFailed, observer: self, function: KeyboardPickerViewController.keyboardDownloadFailed) + + lexicalModelDownloadStartedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadStarted, + observer: self, + function: KeyboardPickerViewController.lexicalModelDownloadStarted) + lexicalModelDownloadCompletedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadCompleted, + observer: self, + function: KeyboardPickerViewController.lexicalModelDownloadCompleted) + lexicalModelDownloadFailedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadFailed, + observer: self, + function: KeyboardPickerViewController.lexicalModelDownloadFailed) + } override func viewWillAppear(_ animated: Bool) { @@ -183,12 +197,14 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { } private func keyboardDownloadStarted(_ keyboards: [InstallableKeyboard]) { + log.info("keyboardDownloadStarted") view.isUserInteractionEnabled = false navigationItem.leftBarButtonItem?.isEnabled = false navigationItem.rightBarButtonItem?.isEnabled = false } private func lexicalModelDownloadStarted(_ lexicalModels: [InstallableLexicalModel]) { + log.info("lexicalModelDownloadStarted") view.isUserInteractionEnabled = false navigationItem.leftBarButtonItem?.isEnabled = false navigationItem.rightBarButtonItem?.isEnabled = false @@ -213,11 +229,8 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { Manager.shared.downloadKeyboard(withID: kbID, languageID: langID, isUpdate: true) } else { loadUserKeyboards() - view.isUserInteractionEnabled = true - navigationItem.leftBarButtonItem?.isEnabled = true - if navigationItem.rightBarButtonItem != nil { - navigationItem.rightBarButtonItem?.isEnabled = true - } + restoreNavigation() + updateQueue = nil let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel label?.text = "Keyboards successfully updated!" @@ -232,11 +245,7 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed), userInfo: nil, repeats: false) - view.isUserInteractionEnabled = true - navigationItem.leftBarButtonItem?.isEnabled = true - if navigationItem.rightBarButtonItem != nil { - navigationItem.rightBarButtonItem?.isEnabled = true - } + restoreNavigation() // Add keyboard. for keyboard in keyboards { @@ -246,9 +255,30 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { navigationController?.popToRootViewController(animated: true) } + log.info("keyboardDownloadCompleted") } - + + private func restoreNavigation() { + view.isUserInteractionEnabled = true + navigationItem.leftBarButtonItem?.isEnabled = true + if navigationItem.rightBarButtonItem != nil { + navigationItem.rightBarButtonItem?.isEnabled = true + } + } + + private func lexicalModelDownloadCompleted(_ lexicalModels: [InstallableLexicalModel]) { + log.info("lexicalModelDownloadCompleted KeyboardPicker") + // Add models. + for lexicalModel in lexicalModels { + Manager.shared.addLexicalModel(lexicalModel) + _ = Manager.shared.registerLexicalModel(lexicalModel) + } + restoreNavigation() + navigationController?.popToRootViewController(animated: true) + } + private func keyboardDownloadFailed(_ notification: KeyboardDownloadFailedNotification) { + log.info("keyboardDownloadFailed KeyboardPicker") view.isUserInteractionEnabled = true navigationItem.leftBarButtonItem?.isEnabled = true if let item = navigationItem.rightBarButtonItem { @@ -272,6 +302,28 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { self.present(alertController, animated: true, completion: nil) } + + private func lexicalModelDownloadFailed(_ notification: LexicalModelDownloadFailedNotification) { + log.info("lexicalModelDownloadFailed") + restoreNavigation() + + let title: String + if view == navigationController?.topViewController?.view { + updateQueue = nil + title = "Lexical Model Update Error" + } else { + title = "Lexical Model Download Error" + } + navigationController?.setToolbarHidden(true, animated: true) + + let alertController = UIAlertController(title: title, message: notification.error.localizedDescription, + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.cancel, + handler: nil)) + + self.present(alertController, animated: true, completion: nil) + } private func switchKeyboard(_ index: Int) { // Switch keyboard and register to user defaults. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift index 8ec8141ab2..8a94073a25 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift @@ -40,6 +40,14 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { super.init(nibName: nil, bundle: nil) lexicalModelRepository.delegate = self } + + init(keyboardRep keyboardRepository: KeyboardRepository, modelRep lexicalModelRepository: LexicalModelRepository) { + self.keyboardRepository = keyboardRepository + self.lexicalModelRepository = lexicalModelRepository + super.init(nibName: nil, bundle: nil) + keyboardRepository.delegate = self + lexicalModelRepository.delegate = self + } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -77,6 +85,7 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { // if no rows to show yet, show a loading indicator if numberOfSections(in: tableView) == 0 { showActivityView() + log.info("didAppear: LanguageViewController, but no rows to show") } else { log.info("didAppear: LanguageViewController") } @@ -357,9 +366,9 @@ extension LanguageViewController: KeyboardRepositoryDelegate { } // MARK: - LexicalModelRepositoryDelegate - //may not need this, as we don't plan ever to fetch the whole lexical model repository (or even the list of all available) + extension LanguageViewController: LexicalModelRepositoryDelegate { - func lexicalModelRepositoryDidFetch(_ repository: LexicalModelRepository) { + func lexicalModelRepositoryDidFetchList(_ repository: LexicalModelRepository) { if let languageDict = repository.languages { languages = languageList(languageDict) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 697bcdeb68..2788ad7b26 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -208,6 +208,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega } private func lexicalModelDownloadCompleted(_ lexicalModels: [InstallableLexicalModel]) { + log.info("lexicalModelDownloadCompleted LexicalModelPicker") if view == navigationController?.topViewController?.view { if updateQueue == nil { return diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 033717e36c..5458496800 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -297,6 +297,9 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // If we have a lexical model for the keyboard's language, activate it. if let valid_models = Storage.active.userDefaults.userLexicalModels(forLanguage: kb.languageID) { if valid_models.count > 0 { + //TODO: this is not quite right: currentLexicalModel might not be for this language + // we need to get a preferred model for this language (or at least check that the current model is for this language) + // then register that one. if let lm = self.currentLexicalModel { _ = Manager.shared.registerLexicalModel(lm) } else { @@ -978,34 +981,29 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat return nil } - func installLexicalModelPackage(downloadedPackageFile: URL) { + // return a lexical model so caller can use it in a downloadSucceeded call + func installLexicalModelPackage(downloadedPackageFile: URL) -> InstallableLexicalModel? { + var installedLexicalModel: InstallableLexicalModel? = nil let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] var destination = documentsDirectory destination.appendPathComponent("temp/\(downloadedPackageFile.lastPathComponent)") KeymanPackage.extract(fileUrl: downloadedPackageFile, destination: destination, complete: { kmp in - if let kmp = kmp { + if let kmp = kmp as! LexicalModelKeymanPackage? { do { try Manager.shared.parseLMKMP(kmp.sourceFolder) - log.info("successfully installed the lexical model from: \(kmp.sourceFolder)") - DispatchQueue.main.async { - self.apiLexicalModelRepository.delegate?.lexicalModelRepositoryDidFetch(self.apiLexicalModelRepository) - } + log.info("successfully parsed the lexical model in: \(kmp.sourceFolder)") + installedLexicalModel = kmp.models[0].installableLexicalModels[0] //this can fail gracefully and not show errors to users try FileManager.default.removeItem(at: downloadedPackageFile) } catch { log.error("Error installing the lexical model: \(error)") - DispatchQueue.main.async { - self.apiLexicalModelRepository.delegate?.lexicalModelRepository(self.apiLexicalModelRepository, didFailFetch: error) - } } } else { log.error("Error extracting the lexical model from the package: \(KMPError.invalidPackage)") - DispatchQueue.main.async { - self.apiLexicalModelRepository.delegate?.lexicalModelRepository(self.apiLexicalModelRepository, didFailFetch: APILexicalModelFetchError.noData) - } } }) + return installedLexicalModel } func downloadLexicalModelPackage(string lexicalModelPackageURLString: String) -> Void { @@ -1020,14 +1018,20 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat error: Error?) { if let error = error { log.error("Failed to fetch lexical model KMP file") - downloadFailed(forKeyboards: [], error: error) + downloadFailed(forLexicalModelPackage: lexicalModelPackageURLString, error: error) } else { do { try data!.write(to: dest) } catch { log.error("Error writing the lexical model download data: \(error)") } - installLexicalModelPackage(downloadedPackageFile: dest) + if let lm = installLexicalModelPackage(downloadedPackageFile: dest) { + downloadSucceeded(forLexicalModel: lm) + } else { + let installError = NSError(domain: "Keyman", code: 0, + userInfo: [NSLocalizedDescriptionKey: "installError"]) + downloadFailed(forLexicalModelPackage: lexicalModelPackageURLString, error: installError ) + } } } @@ -1398,6 +1402,20 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat object: self, value: notification) } + + private func downloadFailed(forLexicalModelPackage packageURL: String, error: Error) { + let notification = LexicalModelDownloadFailedNotification(lmOrLanguageID: packageURL, error: error) + NotificationCenter.default.post(name: Notifications.lexicalModelDownloadFailed, + object: self, + value: notification) + } + + private func downloadSucceeded(forLexicalModel lm: InstallableLexicalModel) { + let notification = LexicalModelDownloadCompletedNotification([lm]) + NotificationCenter.default.post(name: Notifications.lexicalModelDownloadCompleted, + object: self, + value: notification) + } // MARK: - Loading custom keyboards /// Preloads the JS and font files required for a keyboard. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Model/LexicalModelKeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/Model/LexicalModelKeymanPackage.swift index 321d039729..e5c117a914 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Model/LexicalModelKeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Model/LexicalModelKeymanPackage.swift @@ -9,7 +9,7 @@ import Foundation public class LexicalModelKeymanPackage : KeymanPackage { - private var models : [KMPLexicalModel]! + public var models : [KMPLexicalModel]! public override func defaultInfoHtml() -> String { var str = "Found lexical models in package:
" diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 1c60031ed1..9e6779e603 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -344,7 +344,7 @@ extension InstalledLanguagesViewController: KeyboardRepositoryDelegate { // MARK: - LexicalModelRepositoryDelegate //may not need this, as we don't plan ever to fetch the whole lexical model repository (or even the list of all available) extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { - func lexicalModelRepositoryDidFetch(_ repository: LexicalModelRepository) { + func lexicalModelRepositoryDidFetchList(_ repository: LexicalModelRepository) { if let languageDict = repository.languages { languages = languageList(languageDict) } @@ -367,7 +367,7 @@ extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { func showAddKeyboard() { let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) button?.isEnabled = false - let vc = LanguageViewController(Manager.shared.apiKeyboardRepository) + let vc = LanguageViewController(keyboardRep: Manager.shared.apiKeyboardRepository, modelRep: Manager.shared.apiLexicalModelRepository) navigationController?.pushViewController(vc, animated: true) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index bb984a8207..0cfee1056b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -47,21 +47,21 @@ class LanguageSettingsViewController: UITableViewController { } - // MARK: - Table view data source UITableViewDataSource + // MARK: - Table view data source UITableViewDataSource - override func numberOfSections(in tableView: UITableView) -> Int { - return 2 - } + override func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if 0 == section { - // so far as I know, there is always at least one keyboard or else we won't show - let kbct = language.keyboards!.count - return kbct - } else { - return 3 // 4 in future when we can manage a user dictionary - } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if 0 == section { + // so far as I know, there is always at least one keyboard or else we won't show + let kbct = language.keyboards!.count + return kbct + } else { + return 3 // 4 in future when we can manage a user dictionary } + } public func frameAtRightOfCell(cell cellFrame: CGRect, controlSize: CGSize) -> CGRect { let rightOffset = cellFrame.size.width diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index e9bfb558f1..87ef677c1d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -56,7 +56,7 @@ open class SettingsViewController: UITableViewController { itemsArray.append([ "title": "Installed Languages", "subtitle": "0", //count of installed languages as string - "reuseid" : "language" + "reuseid" : "languages" ]) itemsArray.append([ From fee3a6d9fcd486c3f3f52342250b31869b65e798 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 1 Jul 2019 01:42:12 -0500 Subject: [PATCH 38/47] a couple javadoc comments --- .../Settings/InstalledLanguagesViewController.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 9e6779e603..49104ba769 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -342,8 +342,11 @@ extension InstalledLanguagesViewController: KeyboardRepositoryDelegate { } // MARK: - LexicalModelRepositoryDelegate -//may not need this, as we don't plan ever to fetch the whole lexical model repository (or even the list of all available) + extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { + /** lexicalModelRepositoryDidFetchList callback on successful fetching of list of lexical models for a language + * caller should wrap in DispatchQueue.main.async {} if not already on main thread + */ func lexicalModelRepositoryDidFetchList(_ repository: LexicalModelRepository) { if let languageDict = repository.languages { languages = languageList(languageDict) @@ -355,6 +358,9 @@ extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { } } + /** lexicalModelRepositoryDidFetchList callback on failure to fetch list of lexical models for a language + * caller should wrap in DispatchQueue.main.async {} if not already on main thread + */ func lexicalModelRepository(_ repository: LexicalModelRepository, didFailFetch error: Error) { dismissActivityView() showConnectionErrorAlert() From 8db97abfbbce1de5a8d22c0407f481f8177794b0 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 1 Jul 2019 01:42:50 -0500 Subject: [PATCH 39/47] fixes exception thrown for having UI calls made while on background thread --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 5458496800..c34e51c4a4 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -288,7 +288,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat inputViewController.showHelpBubble(afterDelay: 1.5) } - //HERE getAssociatedLexicalModel(langID) and setLexicalModel + // getAssociatedLexicalModel(langID) and setLexicalModel NotificationCenter.default.post(name: Notifications.keyboardChanged, object: self, @@ -374,8 +374,10 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat return true } - /// Adds a new lexical model to the list in the lexical model picker if it doesn't already exist. - /// The lexical model must be downloaded (see `downloadLexicalModel()`) or preloaded (see `preloadLanguageFile()`) + /** Adds a new lexical model to the list in the lexical model picker if it doesn't already exist. + * The lexical model must be downloaded (see `downloadLexicalModel()`) or preloaded (see `preloadLanguageFile()`) + * I believe this is background-thread-safe (no UI done) + */ public func addLexicalModel(_ lexicalModel: InstallableLexicalModel) { let lexicalModelPath = Storage.active.lexicalModelURL(for: lexicalModel).path if !FileManager.default.fileExists(atPath: lexicalModelPath) { @@ -1037,7 +1039,9 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat log.info("downloading lexical model from Keyman cloud: \(lexicalModelKMPURL).") let task = URLSession.shared.dataTask(with: lexicalModelKMPURL) { (data, response, error) in - lexicalModelDownloaded(data: data, response: response, dest: destinationUrl, error: error) + DispatchQueue.main.async { + lexicalModelDownloaded(data: data, response: response, dest: destinationUrl, error: error) + } } task.resume() From 52d8987916666660bdeeecc59134ad6c2f2fd99d Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 8 Jul 2019 22:49:53 -0500 Subject: [PATCH 40/47] logging to track callbacks --- .../LanguagePicker/KeyboardPickerViewController.swift | 9 +++++---- .../LanguagePicker/LanguageDetailViewController.swift | 3 +++ .../LexicalModelPickerViewController.swift | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift index 9e3f4a93fb..4a8d56434c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardPickerViewController.swift @@ -67,6 +67,7 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { observer: self, function: KeyboardPickerViewController.lexicalModelDownloadFailed) + log.info("didLoad: KeyboardPickerViewController (registered lexicalModelDownloadCompleted et al)") } override func viewWillAppear(_ animated: Bool) { @@ -255,7 +256,7 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { navigationController?.popToRootViewController(animated: true) } - log.info("keyboardDownloadCompleted") + log.info("keyboardDownloadCompleted KeyboardPicker") } private func restoreNavigation() { @@ -304,15 +305,15 @@ class KeyboardPickerViewController: UITableViewController, UIAlertViewDelegate { } private func lexicalModelDownloadFailed(_ notification: LexicalModelDownloadFailedNotification) { - log.info("lexicalModelDownloadFailed") + log.info("lexicalModelDownloadFailed KeyboardPicker") restoreNavigation() 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) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift index cafe877470..1aa7ffea90 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift @@ -44,6 +44,7 @@ class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate { forName: Notifications.keyboardDownloadFailed, observer: self, function: LanguageDetailViewController.keyboardDownloadFailed) + log.info("viewDidLoad: LanguageDetailViewController (registered for keyboardDownloadStarted)") } override open func viewWillAppear(_ animated: Bool) { @@ -127,6 +128,7 @@ class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate { } private func keyboardDownloadStarted() { + log.info("keyboardDownloadStarted: LanguageDetailViewController") view.isUserInteractionEnabled = false navigationItem.setHidesBackButton(true, animated: true) @@ -159,6 +161,7 @@ class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate { } private func keyboardDownloadFailed() { + log.info("keyboardDownloadFailed: LanguageDetailViewController") view.isUserInteractionEnabled = true navigationItem.setHidesBackButton(false, animated: true) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift index 2788ad7b26..1e13eab1a3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LexicalModelPickerViewController.swift @@ -58,6 +58,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega forName: Notifications.lexicalModelDownloadFailed, observer: self, function: LexicalModelPickerViewController.lexicalModelDownloadFailed) + + log.info("didLoad: LexicalModelPickerViewController (registered lexicalModelDownloadCompleted et al)") } override func viewWillAppear(_ animated: Bool) { From 1929193634ae29822a10cae55f39bbf68c3e40e6 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 8 Jul 2019 22:50:47 -0500 Subject: [PATCH 41/47] finish adding keyboard and lexical model callbacks so the UI doesn't stay unusable --- .../InstalledLanguagesViewController.swift | 125 ++++++++++++++++-- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 49104ba769..0260f10e16 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -27,8 +27,12 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega private let lexicalModelRepository: LexicalModelRepository? private var keyboardDownloadStartedObserver: NotificationObserver? + private var keyboardDownloadCompletedObserver: NotificationObserver? private var keyboardDownloadFailedObserver: NotificationObserver? - + private var lexicalModelDownloadStartedObserver: NotificationObserver? + private var lexicalModelDownloadCompletedObserver: NotificationObserver? + private var lexicalModelDownloadFailedObserver: NotificationObserver? + init(_ givenLanguages: [String: Language]) { self.installedLanguages = givenLanguages self.keyboardRepository = nil @@ -54,21 +58,41 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega forName: Notifications.keyboardDownloadStarted, observer: self, function: InstalledLanguagesViewController.keyboardDownloadStarted) + keyboardDownloadCompletedObserver = NotificationCenter.default.addObserver( + forName: Notifications.keyboardDownloadCompleted, + observer: self, + function: InstalledLanguagesViewController.keyboardDownloadCompleted) keyboardDownloadFailedObserver = NotificationCenter.default.addObserver( forName: Notifications.keyboardDownloadFailed, observer: self, function: InstalledLanguagesViewController.keyboardDownloadFailed) + lexicalModelDownloadStartedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadStarted, + observer: self, + function: InstalledLanguagesViewController.lexicalModelDownloadStarted) + lexicalModelDownloadCompletedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadCompleted, + observer: self, + function: InstalledLanguagesViewController.lexicalModelDownloadCompleted) + lexicalModelDownloadFailedObserver = NotificationCenter.default.addObserver( + forName: Notifications.lexicalModelDownloadFailed, + observer: self, + function: InstalledLanguagesViewController.lexicalModelDownloadFailed) + if Manager.shared.canAddNewKeyboards { let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.addClicked)) navigationItem.rightBarButtonItem = addButton } + + log.info("viewDidLoad: InstalledLanguagesViewController (registered for keyboardDownloadStarted)") } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) loadUserKeyboards() + loadUserLexicalModels() } override func viewDidAppear(_ animated: Bool) { @@ -209,10 +233,16 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate) } - private func keyboardDownloadStarted() { - view.isUserInteractionEnabled = false - navigationItem.setHidesBackButton(true, animated: true) - + private func restoreNavigation() { + view.isUserInteractionEnabled = true + navigationItem.setHidesBackButton(false, animated: true) + navigationItem.leftBarButtonItem?.isEnabled = true + if navigationItem.rightBarButtonItem != nil { + navigationItem.rightBarButtonItem?.isEnabled = true + } + } + + private func showDownloading(_ downloadLabel: String) { guard let toolbar = navigationController?.toolbar else { return } @@ -225,7 +255,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega label.textColor = UIColor.white label.textAlignment = .center label.center = CGPoint(x: toolbar.frame.width * 0.5, y: toolbar.frame.height * 0.5) - label.text = "Downloading\u{2026}" + label.text = "Downloading \(downloadLabel)\u{2026}" label.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin, .flexibleWidth, .flexibleHeight] label.tag = toolbarLabelTag @@ -244,9 +274,78 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega navigationController?.setToolbarHidden(false, animated: true) } + private func hideDownloading() { + navigationController?.toolbar?.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview() + navigationController?.toolbar?.viewWithTag(toolbarLabelTag)?.removeFromSuperview() + Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed), + userInfo: nil, repeats: false) + } + + private func keyboardDownloadStarted() { + log.info("keyboardDownloadStarted: InstalledLanguagesViewController") + view.isUserInteractionEnabled = false + navigationItem.setHidesBackButton(true, animated: true) + showDownloading("keyboard") + } + + private func lexicalModelDownloadStarted(_ lexicalModels: [InstallableLexicalModel]) { + log.info("lexicalModelDownloadStarted") + showDownloading("dictionary") + } + + private func keyboardDownloadCompleted(_ keyboards: [InstallableKeyboard]) { + log.info("keyboardDownloadCompleted: InstalledLanguagesViewController") + Manager.shared.shouldReloadKeyboard = true + + // Update keyboard version + for keyboard in keyboards { + Manager.shared.updateUserKeyboards(with: keyboard) + } + let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel + label?.text = "Keyboard successfully downloaded!" + hideDownloading() + restoreNavigation() + + // Add keyboard. + for keyboard in keyboards { + Manager.shared.addKeyboard(keyboard) + _ = Manager.shared.setKeyboard(keyboard) + } + + navigationController?.popToRootViewController(animated: true) + } + + private func lexicalModelDownloadCompleted(_ lexicalModels: [InstallableLexicalModel]) { + log.info("lexicalModelDownloadCompleted: InstalledLanguagesViewController") + // Add models. + for lexicalModel in lexicalModels { + Manager.shared.addLexicalModel(lexicalModel) + _ = Manager.shared.registerLexicalModel(lexicalModel) + } + hideDownloading() + restoreNavigation() + navigationController?.popToRootViewController(animated: true) + } + private func keyboardDownloadFailed() { - view.isUserInteractionEnabled = true - navigationItem.setHidesBackButton(false, animated: true) + log.info("keyboardDownloadFailed: InstalledLanguagesViewController") + restoreNavigation() + } + + private func lexicalModelDownloadFailed() { + log.info("lexicalModelDownloadFailed: InstalledLanguagesViewController") + restoreNavigation() + + let title = "Lexical Model Download Error" + navigationController?.setToolbarHidden(true, animated: true) + + let alertController = UIAlertController(title: title, message: "", + preferredStyle: UIAlertControllerStyle.alert) + alertController.addAction(UIAlertAction(title: "OK", + style: UIAlertActionStyle.cancel, + handler: nil)) + + self.present(alertController, animated: true, completion: nil) } func showActivityView() { @@ -282,6 +381,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega let dictKey = "\(kb.languageID)_\(kb.id)" userKeyboards[dictKey] = kb } + tableView.reloadData() } func loadUserLexicalModels() { @@ -294,7 +394,8 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega let dictKey = "\(lm.languageID)_\(lm.id)" userLexicalModels[dictKey] = lm } - } + tableView.reloadData() + } private func isAdded(languageID: String?, keyboardID: String?) -> Bool { guard let languageID = languageID, let keyboardID = keyboardID else { @@ -358,7 +459,7 @@ extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { } } - /** lexicalModelRepositoryDidFetchList callback on failure to fetch list of lexical models for a language + /** lexicalModelRepository didFailFetch callback on failure to fetch list of lexical models for a language * caller should wrap in DispatchQueue.main.async {} if not already on main thread */ func lexicalModelRepository(_ repository: LexicalModelRepository, didFailFetch error: Error) { @@ -366,6 +467,10 @@ extension InstalledLanguagesViewController: LexicalModelRepositoryDelegate { showConnectionErrorAlert() } + @objc func hideToolbarDelayed(_ timer: Timer) { + navigationController?.setToolbarHidden(true, animated: true) + } + @objc func addClicked(_ sender: Any) { showAddKeyboard() } From 892cec8723438731d06f6df463051f6711482c23 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Sun, 14 Jul 2019 22:19:00 -0500 Subject: [PATCH 42/47] only register lexical model if it is valid for the current language --- ios/engine/KMEI/KeymanEngine/Classes/Manager.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index c34e51c4a4..e9931a5309 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -301,11 +301,22 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat // we need to get a preferred model for this language (or at least check that the current model is for this language) // then register that one. if let lm = self.currentLexicalModel { - _ = Manager.shared.registerLexicalModel(lm) + if valid_models.contains(where: { $0.id == lm.id }) { + log.debug("current model \(lm) IS among those valid for the current language \(kb.languageID)") + _ = Manager.shared.registerLexicalModel(lm) + } else { + log.error("current model \(lm) is not among those valid for the current language \(kb.languageID)!") + _ = Manager.shared.registerLexicalModel(valid_models[0]) + } } else { + log.info("no current model, picking first among those valid for the current language \(kb.languageID)") _ = Manager.shared.registerLexicalModel(valid_models[0]) } + } else { + log.debug("empty current model list for the current language \(kb.languageID)") } + } else { + log.debug("no current model list for the current language \(kb.languageID)") } return true From a6f7cd38918ed0be30209efb85f0f48e9403984f Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Mon, 15 Jul 2019 12:55:45 -0500 Subject: [PATCH 43/47] add Plus button to add a keyboard --- .../LanguageSettingsViewController.swift | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 0cfee1056b..22c5eaa2fb 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -8,6 +8,8 @@ import UIKit +private let toolbarButtonTag = 100 + class LanguageSettingsViewController: UITableViewController { let language: Language private var userKeyboards: [String: Language] = [:] @@ -30,19 +32,18 @@ class LanguageSettingsViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() title = "\(language.name) Settings" + log.info("viewDidLoad: LanguageSettingsViewController title: \(title ?? "")") - // Uncomment the following line to preserve selection between presentations - // self.clearsSelectionOnViewWillAppear = false - - // Uncomment the following line to display an Edit button in the navigation bar for this view controller. - // self.navigationItem.rightBarButtonItem = self.editButtonItem + if Manager.shared.canAddNewKeyboards { + let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, + action: #selector(self.addClicked)) + navigationItem.rightBarButtonItem = addButton + } } - override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) -// navigationController?.setToolbarHidden(true, animated: true) - // if no rows to show yet, show a loading indicator + log.info("didAppear: LanguageSettingsViewController") } @@ -203,6 +204,17 @@ class LanguageSettingsViewController: UITableViewController { performAction(for: indexPath) } + @objc func addClicked(_ sender: Any) { + showAddKeyboard() + } + + func showAddKeyboard() { + let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) + button?.isEnabled = false + let vc = LanguageViewController(Manager.shared.apiKeyboardRepository) + navigationController?.pushViewController(vc, animated: true) + } + private func performAction(for indexPath: IndexPath) { switch indexPath.section { case 0: From 7c6eec266ddd4abe633ef4f8fd3de9145d860891 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Tue, 16 Jul 2019 15:07:54 -0500 Subject: [PATCH 44/47] Language Settings can now add a new keyboard via LanguageSpecificViewController, a subclass of LanguageViewController LanguageViewController made more easily subclassable so languages variable doesn't have to be exposed --- .../KeymanEngine.xcodeproj/project.pbxproj | 6 ++ .../LanguageSpecificViewController.swift | 96 +++++++++++++++++++ .../LanguageViewController.swift | 22 ++++- .../LanguageSettingsViewController.swift | 7 +- 4 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 83e9089583..868ed2ca70 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -118,6 +118,8 @@ 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 */; }; + 9A7EEFA222DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */; }; + 9A7EEFA322DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */; }; 9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; }; 9A9CB0822241704800231FB9 /* APILexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */; }; 9A9CB084224170F700231FB9 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */; }; @@ -315,6 +317,7 @@ 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 = ""; }; + 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSpecificViewController.swift; sourceTree = ""; }; 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelPickerViewController.swift; sourceTree = ""; }; 9A9CB0812241704800231FB9 /* APILexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APILexicalModelRepository.swift; sourceTree = ""; }; 9A9CB083224170F700231FB9 /* LexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepository.swift; sourceTree = ""; }; @@ -773,6 +776,7 @@ 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */, C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */, C04C2A6C1F6B7D9A00BA42B6 /* LanguageViewController.swift */, + 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */, 98C0B11817A22DB5007ECAFC /* PickerButtons */, 98C0B11C17A2302E007ECAFC /* TableViewCells */, ); @@ -1114,6 +1118,7 @@ 9A079DDF2231A13A00581263 /* Keyboard.swift in Sources */, 9A31E2C2224AAD9A00D9A491 /* Notifications.swift in Sources */, 9A079DDD2231A11C00581263 /* InstallableKeyboard.swift in Sources */, + 9A7EEFA322DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */, 9A079DD2223194B100581263 /* KeymanEngineTests.swift in Sources */, 9A079E41223B602B00581263 /* LexicalModel.swift in Sources */, 9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */, @@ -1192,6 +1197,7 @@ C040E50E1F85FF8A00901EE4 /* KeyPreviewView.swift in Sources */, 9A079E3A223AFF3C00581263 /* KeyboardKeymanPackage.swift in Sources */, C06D37401F81F5C400F61AE0 /* LanguageDetailViewController.swift in Sources */, + 9A7EEFA222DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */, 9A9CB0862241717C00231FB9 /* LexicalModelRepositoryDelegate.swift in Sources */, C0CB633C1FA6F61500617CDB /* Notifications.swift in Sources */, C0EF3E7B1F95B65300CE9BD4 /* KeymanWebDelegate.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift new file mode 100644 index 0000000000..b6ba97d99e --- /dev/null +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift @@ -0,0 +1,96 @@ +// +// LanguageSpecificViewController.swift +// KeymanEngine +// +// Created by Randy Boring on 7/15/19. +// Copyright © 2019 SIL International. All rights reserved. +// + +import UIKit + +class LanguageSpecificViewController: LanguageViewController { + var language: Language! + + init(_ keyboardRepository: KeyboardRepository, language: Language) { + super.init(keyboardRepository) + self.language = language + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Table view data source +// Do not override if no changes needed +// override func numberOfSections(in tableView: UITableView) -> Int { +// // #warning Incomplete implementation, return the number of sections +// return 0 +// } +// +// override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +// // #warning Incomplete implementation, return the number of rows +// return 0 +// } + + /* + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + + // Configure the cell... + + return cell + } + */ + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // 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. + } + */ + + override func postLanguageLoad(languageDict: [String: Language]) { + super .postLanguageLoad(languageDict: languageDict) + let langIdx = idxOfLanguage(languageID: language.id) + self.showLanguageDetailView(title: self.title!, languageIndex: langIdx) + } + +} diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift index 8a94073a25..812b36d1d0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageViewController.swift @@ -52,12 +52,24 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + + func postLanguageLoad(languageDict: [String: Language]) { + languages = languageList(languageDict) + } + + func idxOfLanguage(languageID: String) -> Int { + let langIdx = self.languages.firstIndex(where: { + $0.id == languageID + }) ?? 0 + return langIdx + } + override func loadView() { super.loadView() if let languageDict = keyboardRepository?.languages { - languages = languageList(languageDict) + self.postLanguageLoad(languageDict: languageDict) } else { + log.info("Fetching repository from API for keyboard download (LanguageViewController)") keyboardRepository?.fetch() } @@ -215,7 +227,7 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { showLanguageDetailView(title: title, languageIndex: indexPath.section) } - private func showLanguageDetailView(title: String, languageIndex: Int) { + func showLanguageDetailView(title: String, languageIndex: Int) { let langDetailView = LanguageDetailViewController(language: languages[languageIndex]) langDetailView.title = title navigationController?.pushViewController(langDetailView, animated: true) @@ -344,7 +356,7 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate { extension LanguageViewController: KeyboardRepositoryDelegate { func keyboardRepositoryDidFetch(_ repository: KeyboardRepository) { if let languageDict = repository.languages { - languages = languageList(languageDict) + self.postLanguageLoad(languageDict: languageDict) } self.dismissActivityView() self.tableView.reloadData() @@ -370,7 +382,7 @@ extension LanguageViewController: KeyboardRepositoryDelegate { extension LanguageViewController: LexicalModelRepositoryDelegate { func lexicalModelRepositoryDidFetchList(_ repository: LexicalModelRepository) { if let languageDict = repository.languages { - languages = languageList(languageDict) + self.postLanguageLoad(languageDict: languageDict) } self.dismissActivityView() self.tableView.reloadData() diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 22c5eaa2fb..c14188de96 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -205,13 +205,14 @@ class LanguageSettingsViewController: UITableViewController { } @objc func addClicked(_ sender: Any) { - showAddKeyboard() + showAddLanguageKeyboard() } - func showAddKeyboard() { + func showAddLanguageKeyboard() { let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton) button?.isEnabled = false - let vc = LanguageViewController(Manager.shared.apiKeyboardRepository) + let vc = LanguageSpecificViewController(Manager.shared.apiKeyboardRepository, language: language) + vc.title = "Add new \(language.name) keyboard" navigationController?.pushViewController(vc, animated: true) } From 8853cb3c00b583a98c1dc82b42638e9dbb5cec46 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Wed, 17 Jul 2019 20:51:54 -0500 Subject: [PATCH 45/47] removed boilerplate comments --- .../LanguageSpecificViewController.swift | 67 ------------------- 1 file changed, 67 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift index b6ba97d99e..66f04adad7 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageSpecificViewController.swift @@ -19,73 +19,6 @@ class LanguageSpecificViewController: LanguageViewController { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - // MARK: - Table view data source -// Do not override if no changes needed -// override func numberOfSections(in tableView: UITableView) -> Int { -// // #warning Incomplete implementation, return the number of sections -// return 0 -// } -// -// override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -// // #warning Incomplete implementation, return the number of rows -// return 0 -// } - - /* - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) - - // Configure the cell... - - return cell - } - */ - - /* - // Override to support conditional editing of the table view. - override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the specified item to be editable. - return true - } - */ - - /* - // Override to support editing the table view. - override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { - if editingStyle == .delete { - // Delete the row from the data source - tableView.deleteRows(at: [indexPath], with: .fade) - } else if editingStyle == .insert { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } - } - */ - - /* - // Override to support rearranging the table view. - override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { - - } - */ - - /* - // Override to support conditional rearranging of the table view. - override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { - // Return false if you do not want the item to be re-orderable. - return true - } - */ - - /* - // MARK: - Navigation - - // 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. - } - */ override func postLanguageLoad(languageDict: [String: Language]) { super .postLanguageLoad(languageDict: languageDict) From 73d2d36fce0223749a30d45d2a9a4296455c6735 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 18 Jul 2019 21:32:48 -0500 Subject: [PATCH 46/47] ignore xcsheme files --- ios/.gitignore | 1 + mac/.gitignore | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/.gitignore b/ios/.gitignore index 55de8601d0..95c1a2de20 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -2,6 +2,7 @@ DerivedData .DS_Store project.xcworkspace xcuserdata +*.xcscheme build keyman/Keyman/KeymanEngine.framework samples/KMSample1/KeymanEngine.framework diff --git a/mac/.gitignore b/mac/.gitignore index a162af22df..8a92eb5143 100644 --- a/mac/.gitignore +++ b/mac/.gitignore @@ -1,10 +1,11 @@ **/DerivedData **/project.xcworkspace **/xcuserdata +**/*.xcscheme Keyman4MacIM/output/* Keyman4MacIM/Keyman4MacIM/Frameworks/** Keyman4MacIM/Pods/** Keyman4Mac/Keyman4Mac/Frameworks/** Carthage/** **/build -**/output \ No newline at end of file +**/output From 777bbe3e1eea227fdfe0b7877a8139b6b624d2c5 Mon Sep 17 00:00:00 2001 From: Randy Boring Date: Thu, 18 Jul 2019 22:33:21 -0500 Subject: [PATCH 47/47] merge and improve --- .../KeymanEngine/Classes/Extension/UserDefaults+Types.swift | 6 ++++++ .../Classes/Settings/SettingsViewController.swift | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift index 2787e4c077..23a7cabd04 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift @@ -191,6 +191,12 @@ public extension UserDefaults { func userLexicalModel(withFullID fullID: FullLexicalModelID) -> InstallableLexicalModel? { return userLexicalModels?.first { $0.fullID == fullID } } + + func userLexicalModelsForLanguage(languageID: String) -> [InstallableLexicalModel]? { + return userLexicalModels?.filter({ + $0.languageID == languageID + }) ?? [] + } var migrationLevel: Int { get { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift index 87ef677c1d..57da4fea48 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/SettingsViewController.swift @@ -281,8 +281,8 @@ open class SettingsViewController: UITableViewController { } else { kbds = [Keyboard(name: k.name, id: k.id, filename: "no filename", isDefault: nil, isRTL: k.isRTL, lastModified: Date(), fileSize: 0, version: k.version, languages: nil, font: nil, oskFont: nil)] } - let userDefaults = Storage.active.userDefaults - let lmListInstalled: [InstallableLexicalModel] = userDefaults.userLexicalModels(forLanguage: k.languageID) ?? [] + let userDefaults : UserDefaults = Storage.active.userDefaults + let lmListInstalled: [InstallableLexicalModel] = userDefaults.userLexicalModelsForLanguage(languageID: l) ?? [] let lmList = installed2API(lmListInstalled) keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: lmList, font: nil, oskFont: nil) }