mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-08 09:55:32 +00:00
330 lines
13 KiB
Swift
330 lines
13 KiB
Swift
/*
|
|
* KeyboardDetailController.swift
|
|
* FirstVoices app
|
|
*
|
|
* License: MIT
|
|
*
|
|
* Copyright © 2022 FirstVoices.
|
|
*
|
|
* Created by Shawn Schantz on 2022-01-14.
|
|
*
|
|
* Class responsible for the display and editing of settings for a single keyboard.
|
|
*
|
|
*/
|
|
|
|
import UIKit
|
|
import KeymanEngine
|
|
|
|
let keyboardsSection = 0
|
|
let languageSettingsSection = 2
|
|
let dictionarySection = 1
|
|
let keyboardInfoSection = 3
|
|
|
|
let keyboardsHeader = "Available Keyboards"
|
|
let languageSettingsHeader = "Dictionary Settings"
|
|
let dictionaryHeader = "Downloadable Dictionaries"
|
|
let keyboardInfoHeader = "Keyboard Information"
|
|
|
|
let keyboardsRowCount = 1
|
|
let languageSettingsRowCount = 2
|
|
let keyboardInfoRowCount = 1
|
|
|
|
let keyboardsRow = 0
|
|
let predictionsRow = 0
|
|
let correctionsRow = 1
|
|
|
|
let versionLabel = "Version"
|
|
let predictionLabel = "Suggest Predictions"
|
|
let correctionLabel = "Suggest Corrections"
|
|
|
|
let noDictionaryMessage = "No dictionaries available"
|
|
|
|
let attributeCellIdentifier = "attributeCell"
|
|
let switchCellIdentifier = "switchCell"
|
|
let labelCellIdentifier = "labelCell"
|
|
|
|
class KeyboardDetailController: UITableViewController {
|
|
private var keyboardRepo = KeyboardRepository.shared
|
|
private var settingsRepo = KeyboardSettingsRepository.shared
|
|
private var lexicalModelRepo = LexicalModelRepository.shared
|
|
|
|
private var keyboardState: KeyboardState? = nil
|
|
private var delegate: RefreshKeyboardCheckmark? = nil
|
|
private var lexicalModels: [FVLexicalModel] = []
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
self.title = keyboardState?.name
|
|
|
|
self.navigationController?.toolbar.isHidden = false
|
|
|
|
// get lexical models to display in dictionary section
|
|
if let languageTag = self.keyboardState?.languageTag {
|
|
lexicalModels = lexicalModelRepo.getAvailableLexicalModels(languageTag: languageTag)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Used to display the values for the keyboard that was tapped, causing the segue to the detail view.
|
|
*/
|
|
func configure(delegate: RefreshKeyboardCheckmark, keyboard: KeyboardState) {
|
|
self.delegate = delegate
|
|
self.keyboardState = keyboard
|
|
}
|
|
|
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
return 4
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView,
|
|
titleForHeaderInSection section: Int) -> String? {
|
|
var header = ""
|
|
switch section {
|
|
case keyboardsSection:
|
|
header = keyboardsHeader
|
|
case languageSettingsSection:
|
|
header = languageSettingsHeader
|
|
case dictionarySection:
|
|
header = dictionaryHeader
|
|
case keyboardInfoSection:
|
|
header = keyboardInfoHeader
|
|
default:
|
|
header = "Undefined"
|
|
}
|
|
return header
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
var numberOfRows = 0;
|
|
switch section {
|
|
case keyboardsSection:
|
|
numberOfRows = keyboardsRowCount
|
|
case languageSettingsSection:
|
|
numberOfRows = languageSettingsRowCount
|
|
case dictionarySection:
|
|
numberOfRows = max(1, self.lexicalModels.count)
|
|
case keyboardInfoSection:
|
|
numberOfRows = keyboardInfoRowCount
|
|
default:
|
|
numberOfRows = 0
|
|
}
|
|
return numberOfRows
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
var tableCell: UITableViewCell? = nil
|
|
|
|
switch indexPath.section {
|
|
case keyboardsSection:
|
|
tableCell = configureKeyboardCell()
|
|
case languageSettingsSection:
|
|
if indexPath.row == predictionsRow {
|
|
tableCell = configurePredictionsSwitchCell()
|
|
} else if indexPath.row == correctionsRow {
|
|
tableCell = configureCorrectionsSwitchCell()
|
|
}
|
|
case dictionarySection:
|
|
if self.lexicalModels.isEmpty {
|
|
tableCell = configureNoDictionaryCell()
|
|
} else {
|
|
tableCell = configureDictionaryCell(index: indexPath.row)
|
|
}
|
|
case keyboardInfoSection:
|
|
tableCell = configureVersionCell()
|
|
|
|
default:
|
|
tableCell = tableView.dequeueReusableCell(withIdentifier: "labelCell")
|
|
tableCell?.textLabel!.text = "n/a"
|
|
}
|
|
return tableCell!
|
|
}
|
|
|
|
func configureKeyboardCell() -> UITableViewCell {
|
|
let switchCell = tableView.dequeueReusableCell(withIdentifier: switchCellIdentifier) as! KeyboardDetailCell
|
|
let keyboardAction: Callback = { (on) in
|
|
var updatedKeyboardState = false
|
|
|
|
// call Keyman Engine to install or remove the keyboard
|
|
if on {
|
|
updatedKeyboardState = self.keyboardRepo.installKeyboard(keyboard: self.keyboardState!.definition)
|
|
} else {
|
|
updatedKeyboardState = self.keyboardRepo.removeKeyboard(keyboard: self.keyboardState!.definition)
|
|
}
|
|
|
|
if (updatedKeyboardState) {
|
|
// update state and write to local settings storage
|
|
self.keyboardState!.isActive = on
|
|
self.settingsRepo.saveKeyboardState(state: self.keyboardState!)
|
|
|
|
// update the on/off state of dependent switches
|
|
self.updateDictionarySettingsState(animated: true)
|
|
self.delegate?.refreshCheckmark()
|
|
} else {
|
|
// install or remove failed, set switch back to previous state
|
|
let keyboardsIndex = IndexPath(row: keyboardsRow, section: keyboardsSection)
|
|
|
|
self.updateCellSwitchState(index: keyboardsIndex,
|
|
on: self.keyboardState!.isActive,
|
|
animated: true,
|
|
available: true)
|
|
}
|
|
}
|
|
switchCell.configure(label: (self.keyboardState?.name)!, on: self.keyboardState!.isActive, available: true,
|
|
callback: keyboardAction)
|
|
return switchCell
|
|
}
|
|
|
|
func reportError(message: String) {
|
|
let alertController = UIAlertController(title: title, message: message,
|
|
preferredStyle: UIAlertController.Style.alert)
|
|
alertController.addAction(UIAlertAction(title: "OK",
|
|
style: UIAlertAction.Style.cancel,
|
|
handler: nil))
|
|
|
|
self.present(alertController, animated: true, completion: nil)
|
|
print(message)
|
|
// TODO: send the error message + call stack through to us
|
|
// some reporting mechanism
|
|
}
|
|
|
|
func configurePredictionsSwitchCell() -> UITableViewCell {
|
|
let switchCell = tableView.dequeueReusableCell(withIdentifier: switchCellIdentifier) as! KeyboardDetailCell
|
|
let predictionAction: Callback = { (on) in
|
|
if self.lexicalModelRepo.applyPredictionSettings(languageId: self.keyboardState!.languageTag, modelId: self.keyboardState!.lexicalModelId!, on: on) {
|
|
self.keyboardState!.updatePredictions(on: on)
|
|
self.settingsRepo.saveKeyboardState(state: self.keyboardState!)
|
|
self.updateDictionarySettingsState(animated: true)
|
|
}
|
|
}
|
|
switchCell.configure(label: predictionLabel, on: self.keyboardState!.suggestPredictions, available: self.keyboardState!.canSuggestPredictions(),
|
|
callback: predictionAction)
|
|
return switchCell
|
|
}
|
|
|
|
func configureCorrectionsSwitchCell() -> UITableViewCell {
|
|
let switchCell = tableView.dequeueReusableCell(withIdentifier: switchCellIdentifier) as! KeyboardDetailCell
|
|
let correctionAction: Callback = { (on) in
|
|
if (self.lexicalModelRepo.applyCorrectionSettings(languageId: self.keyboardState!.languageTag, modelId: self.keyboardState!.lexicalModelId!, on: on)) {
|
|
self.keyboardState?.suggestCorrections = on
|
|
self.settingsRepo.saveKeyboardState(state: self.keyboardState!)
|
|
}
|
|
}
|
|
switchCell.configure(label: correctionLabel, on: self.keyboardState!.suggestCorrections, available: self.keyboardState!.canSuggestCorrections(),
|
|
callback: correctionAction)
|
|
return switchCell
|
|
}
|
|
|
|
func configureNoDictionaryCell() -> UITableViewCell {
|
|
let labelCell = tableView.dequeueReusableCell(withIdentifier: labelCellIdentifier)
|
|
labelCell?.textLabel!.text = noDictionaryMessage
|
|
return labelCell!
|
|
}
|
|
|
|
func configureDictionaryCell(index: Int) -> UITableViewCell {
|
|
let switchCell = tableView.dequeueReusableCell(withIdentifier: switchCellIdentifier) as! KeyboardDetailCell
|
|
|
|
if (self.lexicalModels.isEmpty) {
|
|
print("error, invalid cell: there are no lexical models available for this langauge")
|
|
return configureNoDictionaryCell()
|
|
}
|
|
let thisDictionary = self.lexicalModels[index]
|
|
|
|
let savedModel = (self.keyboardState?.lexicalModelName)!
|
|
let modelEnabled = thisDictionary.name == savedModel
|
|
|
|
let dictionaryAction: Callback = { (on) in
|
|
let lexicalModelName = thisDictionary.name
|
|
let lexicalModelId = thisDictionary.id
|
|
|
|
if on {
|
|
let message = "As you type in your language, the FirstVoices dictionary will provide suggestions.\n\nWould you like to install this dictionary?"
|
|
let alert = UIAlertController(title: "\(lexicalModelName)", message: message, preferredStyle: .alert)
|
|
|
|
let installAction = UIAlertAction(title: NSLocalizedString("Install", comment: "Default action"), style: .default, handler: { _ in
|
|
|
|
let toolbar = self.navigationController?.toolbar as? DownloadStatusToolbar
|
|
toolbar?.displayStatus(true)
|
|
|
|
ResourceDownloadManager.shared.downloadLexicalModelsForLanguageIfExists(languageID: self.keyboardState!.languageTag) { package, error in
|
|
if error != nil {
|
|
print("lexical model download failed: \(error!)")
|
|
}
|
|
|
|
toolbar?.hideStatus()
|
|
|
|
let installed = self.lexicalModelRepo.installLexicalModel(package: package!, keyboardState: self.keyboardState!, modelId: lexicalModelId)
|
|
if (installed) {
|
|
self.keyboardState?.selectDictionary(lexicalModel: thisDictionary)
|
|
self.settingsRepo.saveKeyboardState(state: self.keyboardState!)
|
|
self.updateDictionarySettingsState(animated: true)
|
|
}
|
|
}
|
|
})
|
|
alert.addAction(installAction)
|
|
|
|
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
|
|
switchCell.detailSwitch.isOn = false
|
|
}
|
|
alert.addAction(cancelAction)
|
|
self.present(alert, animated: true, completion: nil)
|
|
} else {
|
|
if self.lexicalModelRepo.disableLexicalModel(keyboardState: self.keyboardState!, modelId: lexicalModelId) {
|
|
self.keyboardState?.clearDictionary()
|
|
self.settingsRepo.saveKeyboardState(state: self.keyboardState!)
|
|
self.updateDictionarySettingsState(animated: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
switchCell.configure(label: thisDictionary.name, on: modelEnabled, available: self.keyboardState!.canSelectDictionary(),
|
|
callback: dictionaryAction)
|
|
return switchCell
|
|
}
|
|
|
|
func configureVersionCell() -> UITableViewCell {
|
|
let versionCell = tableView.dequeueReusableCell(withIdentifier: attributeCellIdentifier)
|
|
versionCell!.textLabel!.text = versionLabel
|
|
let versionText = self.keyboardState?.version
|
|
versionCell!.detailTextLabel!.text = versionText
|
|
return versionCell!
|
|
}
|
|
|
|
func updateCellSwitchAvailability(index: IndexPath, available: Bool) {
|
|
if let cell:KeyboardDetailCell = self.tableView.cellForRow(at: index) as? KeyboardDetailCell {
|
|
cell.updateSwitchAvailability(available: available)
|
|
}
|
|
}
|
|
|
|
func updateCellSwitchState(index: IndexPath, on: Bool, animated: Bool, available: Bool) {
|
|
if let cell:KeyboardDetailCell = self.tableView.cellForRow(at: index) as? KeyboardDetailCell {
|
|
cell.updateSwitchState(on: on, animated: animated, available: available)
|
|
}
|
|
}
|
|
|
|
func updateDictionarySettingsState(animated: Bool) {
|
|
let keyboardsIndex = IndexPath(row: keyboardsRow, section: keyboardsSection)
|
|
self.updateCellSwitchState(index: keyboardsIndex,
|
|
on: self.keyboardState!.isActive,
|
|
animated: animated,
|
|
available: true)
|
|
|
|
for rowNumber in 0...max(0, lexicalModels.count-1) {
|
|
let dictionaryIndex = IndexPath(row: rowNumber, section: dictionarySection)
|
|
self.updateCellSwitchState(index: dictionaryIndex,
|
|
on: self.keyboardState!.isDictionarySelected(),
|
|
animated: animated,
|
|
available: self.keyboardState!.canSelectDictionary())
|
|
}
|
|
|
|
let predictionIndex = IndexPath(row: predictionsRow, section: languageSettingsSection)
|
|
self.updateCellSwitchState(index: predictionIndex,
|
|
on: self.keyboardState!.suggestPredictions,
|
|
animated: animated,
|
|
available: self.keyboardState!.canSuggestPredictions())
|
|
let correctionIndex = IndexPath(row: correctionsRow, section: languageSettingsSection)
|
|
self.updateCellSwitchState(index: correctionIndex,
|
|
on: self.keyboardState!.suggestCorrections,
|
|
animated: animated,
|
|
available: self.keyboardState!.canSuggestCorrections())
|
|
}
|
|
}
|