mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 09:25:33 +00:00
209 lines
8.4 KiB
Swift
209 lines
8.4 KiB
Swift
/*
|
|
* LexicalModelRepository.swift
|
|
* FirstVoices app
|
|
*
|
|
* License: MIT
|
|
*
|
|
* Copyright © 2022 FirstVoices.
|
|
*
|
|
* Created by Shawn Schantz on 2022-01-27.
|
|
*
|
|
* Class to call the Keyman API to query about lexical models for a given language.
|
|
*
|
|
*/
|
|
|
|
import UIKit
|
|
import KeymanEngine
|
|
|
|
class LexicalModelRepository {
|
|
|
|
private static let keymanLexicalModelApiUrl = "https://api.keyman.com/model?q=bcp47:"
|
|
|
|
// these strings match those in KeymanEngine so that we can share data across UserDefaults (hack!)
|
|
private static let userPredictionSettings = "UserPredictionEnablementSettings"
|
|
private static let userCorrectionSettings = "UserCorrectionEnablementSettings"
|
|
|
|
static let shared: LexicalModelRepository = {
|
|
let instance = LexicalModelRepository()
|
|
return instance
|
|
}()
|
|
|
|
private init() {}
|
|
|
|
func getAvailableLexicalModels(languageTag: String) -> [FVLexicalModel] {
|
|
var modelArray: [FVLexicalModel] = []
|
|
let keymanApiUrl: URL = URL.init(string: "\(LexicalModelRepository.keymanLexicalModelApiUrl)\(languageTag)")!
|
|
|
|
do {
|
|
let lexicalModelData = try Data(contentsOf: keymanApiUrl, options: NSData.ReadingOptions())
|
|
|
|
do {
|
|
// make sure this JSON is in the format we expect
|
|
if let jsonArray = try JSONSerialization.jsonObject(with: lexicalModelData, options: []) as? [[String : Any]] {
|
|
// try to read array
|
|
if !jsonArray.isEmpty {
|
|
let jsonModelMap = jsonArray.first
|
|
let name = jsonModelMap!["name"] as! String
|
|
let modelId = jsonModelMap!["id"] as! String
|
|
let packageUrl = jsonModelMap!["packageFilename"] as! String
|
|
let version = jsonModelMap!["version"] as! String
|
|
let model = FVLexicalModel(name: name, id: modelId, packageUrl: packageUrl, languageTag: languageTag, version: version)
|
|
modelArray.append(model)
|
|
}
|
|
}
|
|
} catch let error as NSError {
|
|
print("Failed to load: \(error.localizedDescription)")
|
|
}
|
|
print(lexicalModelData)
|
|
} catch {
|
|
print(error)
|
|
}
|
|
|
|
// TODO: delete commented-out test code when a keyboard with multiple dictionaries is available
|
|
/*
|
|
// test code for multi dictionary support since no real data exists for this
|
|
// add two fake dictionaries for single language
|
|
if (languageTag == "ikt-latn") {
|
|
var name = "northern dialect"
|
|
var modelId = "nrc.str.sencoten"
|
|
var packageUrl = "https://keyman.com/go/package/download/model/nrc.str.sencoten?version=1.0.5&update=1"
|
|
var version = "1.01"
|
|
var model = FVLexicalModel(name: name, id: modelId, packageUrl: packageUrl, languageTag: languageTag, version: version)
|
|
modelArray.append(model)
|
|
name = "southern dialect"
|
|
modelId = "nrc.str.sencoten"
|
|
packageUrl = "https://keyman.com/go/package/download/model/nrc.str.sencoten?version=1.0.5&update=1"
|
|
version = "1.03"
|
|
model = FVLexicalModel(name: name, id: modelId, packageUrl: packageUrl, languageTag: languageTag, version: version)
|
|
modelArray.append(model)
|
|
}
|
|
*/
|
|
return modelArray
|
|
}
|
|
|
|
/*
|
|
* install = 1) download lexical model and 2) turn on prediction and corrections by default
|
|
*/
|
|
func installLexicalModel(package: LexicalModelKeymanPackage, keyboardState: KeyboardState, modelId: String) -> Bool {
|
|
let added = Manager.shared.addLexicalModel(lexicalModelId: modelId, languageId: keyboardState.languageTag, from: package)
|
|
|
|
if (!added) {
|
|
return false
|
|
}
|
|
|
|
// set as preferred lexical model (same as done in Keyman switchLexicalModel())
|
|
let defaults: UserDefaults = FVShared.userDefaults()
|
|
defaults.set(preferredLexicalModelID: modelId, forKey: keyboardState.languageTag)
|
|
|
|
// call Keyman once after enabling both prediction and correction flags in UserDefaults
|
|
self.writePredictionSettings(languageId: keyboardState.languageTag, modelId: modelId, on: true)
|
|
self.writeCorrectionSettings(languageId: keyboardState.languageTag, modelId: modelId, on: true)
|
|
|
|
let applied = self.applyLexicalModelSettings(languageId: keyboardState.languageTag, modelId: modelId)
|
|
|
|
return applied
|
|
}
|
|
|
|
func disableLexicalModel(keyboardState: KeyboardState, modelId: String) -> Bool {
|
|
let removed = Manager.shared.removeLexicalModel(lexicalModelId: modelId, languageId: keyboardState.languageTag)
|
|
return removed
|
|
}
|
|
|
|
func downloadModel(keyboardState: KeyboardState, modelId: String) {
|
|
Manager.shared.downloadLexicalModel(withID: modelId, languageID: keyboardState.languageTag,
|
|
isUpdate: true, fetchRepositoryIfNeeded: true)
|
|
}
|
|
|
|
// TODO: delete functions for passing data via UserDefaults to KeymanEngine, replace with direct API call
|
|
/*
|
|
* write prediction settings to UserDefaults but do not apply
|
|
*/
|
|
func writePredictionSettings(languageId: String, modelId: String, on: Bool) {
|
|
self.writeLexicalModelSettings(userDefaultsKey: LexicalModelRepository.userPredictionSettings,
|
|
languageId: languageId, modelId: modelId, on: on)
|
|
}
|
|
|
|
// TODO: delete functions for passing data via UserDefaults to KeymanEngine, replace with direct API call
|
|
/*
|
|
* write correction settings to UserDefaults but do not apply
|
|
*/
|
|
func writeCorrectionSettings(languageId: String, modelId: String, on: Bool) {
|
|
self.writeLexicalModelSettings(userDefaultsKey: LexicalModelRepository.userCorrectionSettings,
|
|
languageId: languageId, modelId: modelId, on: on)
|
|
}
|
|
|
|
// TODO: delete functions for passing data via UserDefaults to KeymanEngine, replace with direct API call
|
|
/*
|
|
* write prediction settings to UserDefaults and apply them
|
|
*/
|
|
func applyPredictionSettings(languageId: String, modelId: String, on: Bool) -> Bool {
|
|
self.writeLexicalModelSettings(userDefaultsKey: LexicalModelRepository.userPredictionSettings,
|
|
languageId: languageId, modelId: modelId, on: on)
|
|
|
|
// automatically turn off corrections when predictions are turned off
|
|
if (!on) {
|
|
self.writeLexicalModelSettings(userDefaultsKey: LexicalModelRepository.userCorrectionSettings,
|
|
languageId: languageId, modelId: modelId, on: false)
|
|
|
|
}
|
|
return self.applyLexicalModelSettings(languageId: languageId, modelId: modelId)
|
|
}
|
|
|
|
// TODO: delete functions for passing data via UserDefaults to KeymanEngine, replace with direct API call
|
|
/*
|
|
* write correction settings to UserDefaults and apply them
|
|
*/
|
|
func applyCorrectionSettings(languageId: String, modelId: String, on: Bool) -> Bool {
|
|
self.writeLexicalModelSettings(userDefaultsKey: LexicalModelRepository.userCorrectionSettings,
|
|
languageId: languageId, modelId: modelId, on: on)
|
|
return self.applyLexicalModelSettings(languageId: languageId, modelId: modelId)
|
|
}
|
|
|
|
// TODO: delete functions for passing data via UserDefaults to KeymanEngine, replace with direct API call
|
|
/*
|
|
* method to update prediction or correction UserDefaults as specified
|
|
*/
|
|
func writeLexicalModelSettings(userDefaultsKey: String, languageId: String, modelId: String, on: Bool) {
|
|
var newSettings: [String:Bool] = [:]
|
|
|
|
// get current settings for all dictionaries, if they exist in UserDefaults
|
|
let sharedData: UserDefaults = FVShared.userDefaults()
|
|
|
|
if let settings = sharedData.dictionary(forKey: userDefaultsKey) as? [String : Bool] {
|
|
if settings[languageId] != nil {
|
|
newSettings = settings
|
|
}
|
|
}
|
|
newSettings[languageId] = on
|
|
|
|
sharedData.set(newSettings, forKey: userDefaultsKey)
|
|
}
|
|
|
|
// TODO: delete call to registerLexicalModel, replace with direct API call
|
|
/*
|
|
* call to KeymanEngine registerLexicalModel to force new settings to be applied
|
|
*/
|
|
func applyLexicalModelSettings(languageId: String, modelId: String) -> Bool {
|
|
let registered = Manager.shared.registerLexicalModel(lexicalModelId: modelId, languageId: languageId)
|
|
if (!registered) {
|
|
print("Could not register lexical model for prediction settings; lang=\(languageId), lexical model ID=\(modelId)")
|
|
}
|
|
return registered
|
|
}
|
|
}
|
|
|
|
class FVLexicalModel {
|
|
let name: String
|
|
let id: String
|
|
let packageUrl: URL
|
|
let languageTag: String
|
|
let version: String
|
|
|
|
internal init(name: String, id: String, packageUrl: String, languageTag: String, version: String) {
|
|
self.name = name
|
|
self.id = id
|
|
self.packageUrl = URL(string: packageUrl)!
|
|
self.languageTag = languageTag
|
|
self.version = version
|
|
}
|
|
}
|