spiegel-keyman/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/LanguageDetailViewController.swift
2020-01-27 11:57:55 +07:00

256 lines
9.2 KiB
Swift

//
// LanguageDetailViewController.swift
// KeymanEngine
//
// Created by Gabriel Wong on 2017-09-14.
// Copyright © 2017 SIL International. All rights reserved.
//
import UIKit
private let toolbarButtonTag = 100
private let toolbarLabelTag = 101
private let toolbarActivityIndicatorTag = 102
class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate {
private let activityViewTag = -2
private var userKeyboards: [String: InstallableKeyboard] = [:]
private var isUpdate = false
private var language: Language
private var keyboardRepository: KeyboardRepository?
private var pendingFetch: Bool = false
private var keyboardDownloadStartedObserver: NotificationObserver?
private var keyboardDownloadFailedObserver: NotificationObserver?
convenience init(language: Language) {
self.init(nil, language: language)
}
init(_ keyboardRepository: KeyboardRepository?, language: Language) {
self.language = language
super.init(nibName: nil, bundle: nil)
self.keyboardRepository = keyboardRepository
keyboardRepository?.delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
if keyboardRepository != nil {
if let languageDict = keyboardRepository?.languages {
self.postLanguageLoad(languageDict: languageDict)
} else {
log.info("Fetching repository from API for keyboard download (LanguageDetailViewController)")
pendingFetch = true
keyboardRepository?.fetch()
}
}
loadUserKeyboards()
}
func postLanguageLoad(languageDict: [String: Language]) {
pendingFetch = false
if let language = languageDict[self.language.id] {
self.language = language
// Case 1 - called immediately during loadView() - UI is built after the load, so we're fine.
// Case 2 - called by keyboardRepositoryDidFetch() - UI updated by self.tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
keyboardDownloadStartedObserver = NotificationCenter.default.addObserver(
forName: Notifications.keyboardDownloadStarted,
observer: self,
function: LanguageDetailViewController.keyboardDownloadStarted)
keyboardDownloadFailedObserver = NotificationCenter.default.addObserver(
forName: Notifications.keyboardDownloadFailed,
observer: self,
function: LanguageDetailViewController.keyboardDownloadFailed)
log.info("viewDidLoad: LanguageDetailViewController (registered for keyboardDownloadStarted)")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.setToolbarHidden(true, animated: true)
// if no rows to show yet, show a loading indicator
if pendingFetch {
showActivityView()
log.info("didAppear: LanguageDetailViewController, but fetch is not yet complete")
} else {
log.info("didAppear: LanguageDetailViewController")
}
}
// MARK: - Table view data source UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return language.keyboards!.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) {
return cell
}
let cell = KeyboardNameTableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
let selectionColor = UIView()
selectionColor.backgroundColor = Colors.selectionPrimary
cell.selectedBackgroundView = selectionColor
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let keyboard = language.keyboards![indexPath.section]
let cell = cell as! KeyboardNameTableViewCell
cell.indexPath = indexPath
cell.textLabel?.text = keyboard.name
if isAdded(languageID: language.id, keyboardID: keyboard.id) {
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 = ResourceDownloadManager.shared.stateForKeyboard(withID: keyboard.id)
cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.isSelected = false
let keyboardIndex = indexPath.section
let keyboard = language.keyboards![keyboardIndex]
let state = ResourceDownloadManager.shared.stateForKeyboard(withID: keyboard.id)
if state != .downloading {
if state == .needsDownload {
isUpdate = false
} else {
isUpdate = true
}
let alertController = UIAlertController(title: "\(language.name): \(keyboard.name)",
message: "Would you like to download this keyboard?",
preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "Cancel",
style: UIAlertAction.Style.cancel,
handler: nil))
alertController.addAction(UIAlertAction(title: "Download",
style: UIAlertAction.Style.default,
handler: {_ in self.downloadHandler(keyboardIndex)} ))
self.present(alertController, animated: true, completion: nil)
}
}
func downloadHandler(_ keyboardIndex: Int) {
ResourceDownloadManager.shared.downloadKeyboard(withID: language.keyboards![keyboardIndex].id,
languageID: language.id, isUpdate: isUpdate)
}
func showActivityView() {
view.isUserInteractionEnabled = false
let indicatorView = UIActivityIndicatorView(style: .whiteLarge)
let activityView = UIView(frame: indicatorView.bounds.insetBy(dx: -10.0, dy: -10.0))
activityView.backgroundColor = Colors.spinnerBackground
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
}
private func showConnectionErrorAlert() {
Alerts.showConnectionErrorAlert(in: self, handler: Alerts.popToNavigationRootHandler(for: self))
}
private func keyboardDownloadStarted() {
log.info("keyboardDownloadStarted: LanguageDetailViewController")
view.isUserInteractionEnabled = false
navigationItem.setHidesBackButton(true, animated: true)
}
private func keyboardDownloadFailed() {
log.info("keyboardDownloadFailed: LanguageDetailViewController")
Alerts.showDownloadErrorAlert(in: self, handler: Alerts.popToNavigationRootHandler(for: self))
view.isUserInteractionEnabled = true
navigationItem.setHidesBackButton(false, animated: true)
}
private func loadUserKeyboards() {
guard let userKbList = Storage.active.userDefaults.userKeyboards, !userKbList.isEmpty else {
userKeyboards = [:]
return
}
userKeyboards = [:]
for kb in userKbList {
let dictKey = "\(kb.languageID)_\(kb.id)"
userKeyboards[dictKey] = kb
}
}
private func isAdded(languageID: String?, keyboardID: String?) -> Bool {
guard let languageID = languageID, let keyboardID = keyboardID else {
return false
}
return userKeyboards["\(languageID)_\(keyboardID)"] != nil
}
}
// MARK: - KeyboardRepositoryDelegate
extension LanguageDetailViewController: KeyboardRepositoryDelegate {
func keyboardRepositoryDidFetch(_ repository: KeyboardRepository) {
if let languageDict = repository.languages {
self.postLanguageLoad(languageDict: 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
}
}
}