-
Notifications
You must be signed in to change notification settings - Fork 150
/
Copy pathPreferencesTableViewController.swift
399 lines (345 loc) · 17.6 KB
/
PreferencesTableViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
//
// PreferencesTableViewController.swift
// OpenGpxTracker
//
// Created by merlos on 24/10/15.
//
// Localized by nitricware on 19/08/19.
//
import Foundation
import UIKit
import CoreLocation
import MapCache
import CoreServices
/// Units Section Id in PreferencesTableViewController
let kUnitsSection = 0
/// Screen always on section id
let kScreenSection = 1
/// Cache Section Id in PreferencesTableViewController
let kCacheSection = 2
/// Map Source Section Id in PreferencesTableViewController
let kMapSourceSection = 3
/// Activity Type Section Id in PreferencesTableViewController
let kActivityTypeSection = 4
/// Default Name Section Id in PreferencesTableViewController
let kDefaultNameSection = 5
/// GPX Files Location Section Id in PreferencesTableViewController
let kGPXFilesLocationSection = 6
/// Cell Id of the Use Imperial units in UnitsSection
let kUseImperialUnitsCell = 0
/// Cell Id of the keepScreenAlwaysOnl units in ScreenSection
let kKeepScreenAlwaysOnCell = 0
/// Cell Id for Use offline cache in CacheSection of PreferencesTableViewController
let kUseOfflineCacheCell = 0
/// Cell Id for Clear cache in CacheSection of PreferencesTableViewController
let kClearCacheCell = 1
///
/// There are two preferences available:
/// * use or not cache
/// * select the map source (tile server)
///
/// Preferences are kept on UserDefaults with the keys `kDefaultKeyTileServerInt` (Int)
/// and `kDefaultUseCache`` (Bool)
///
class PreferencesTableViewController: UITableViewController, UINavigationBarDelegate, UIDocumentPickerDelegate {
/// Delegate for this table view controller.
weak var delegate: PreferencesTableViewControllerDelegate?
/// Global Preferences
var preferences: Preferences = Preferences.shared
var cache: MapCache = MapCache(withConfig: MapCacheConfig(withUrlTemplate: ""))
// Compute once, better performance for scrolling table view (reuse)
/// Store cached size for reuse.
var cachedSize = String()
/// Does the following:
/// 1. Defines the areas for navBar and the Table view
/// 2. Sets the title
/// 3. Loads the Preferences from defaults
override func viewDidLoad() {
super.viewDidLoad()
let navBarFrame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 64)
self.tableView.frame = CGRect(x: navBarFrame.width + 1, y: 0, width: self.view.frame.width, height:
self.view.frame.height - navBarFrame.height)
self.title = NSLocalizedString("PREFERENCES", comment: "no comment")
let shareItem = UIBarButtonItem(title: NSLocalizedString("DONE", comment: "no comment"),
style: UIBarButtonItem.Style.plain, target: self,
action: #selector(PreferencesTableViewController.closePreferencesTableViewController))
self.navigationItem.rightBarButtonItems = [shareItem]
// Set a temporary value for cachedSize
cachedSize = NSLocalizedString("CALCULATING", comment: "Calculating cache")
print("PreferencesTableViewConroller: Starting cache calculation")
// Perform the file size calculation asynchronously
DispatchQueue.global(qos: .background).async {
let fileSize = self.cache.diskCache.fileSize ?? 0
self.cachedSize = Int(fileSize).asFileSize()
// Update the cachedSize on the main thread once the operation is complete
DispatchQueue.main.async {
print("PreferencesTableViewController: Completing cache calculation")
if let cell = self.tableView.cellForRow(at: IndexPath(row: kUseOfflineCacheCell, section: kCacheSection)) {
cell.detailTextLabel?.text = self.cachedSize
}
}
}
}
/// Close this controller.
@objc func closePreferencesTableViewController() {
print("closePreferencesTableViewController()")
self.dismiss(animated: true, completion: { () -> Void in
})
}
/// Loads data
override func viewDidAppear(_ animated: Bool) {
self.tableView.reloadData()
}
/// Does nothing for now.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
/// Returns 4 sections: Units, Cache, Map Source, Activity Type
override func numberOfSections(in tableView: UITableView?) -> Int {
// Return the number of sections.
return 7
}
/// Returns the title of the existing sections.
/// Uses `kCacheSection`, `kUnitsSection`, `kMapSourceSection` and `kActivityTypeSection`
/// for deciding which is the section title
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case kUnitsSection: return NSLocalizedString("UNITS", comment: "no comment")
case kCacheSection: return NSLocalizedString("CACHE", comment: "no comment")
case kMapSourceSection: return NSLocalizedString("MAP_SOURCE", comment: "no comment")
case kActivityTypeSection: return NSLocalizedString("ACTIVITY_TYPE", comment: "no comment")
case kDefaultNameSection: return NSLocalizedString("DEFAULT_NAME_SECTION", comment: "no comment")
case kGPXFilesLocationSection: return NSLocalizedString("GPX_FILES_FOLDER", comment: "no comment")
case kScreenSection: return NSLocalizedString("SCREEN", comment: "no comment")
default: fatalError("Unknown section")
}
}
/// For section `kCacheSection` returns 2, for `kUnitsSection` returns 1,
/// for `kMapSourceSection` returns the number of tile servers defined in `GPXTileServer`,
/// and for kActivityTypeSection returns `CLActivityType.count`
override func tableView(_ tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
switch section {
case kCacheSection: return 2
case kUnitsSection: return 1
case kMapSourceSection: return GPXTileServer.count
case kActivityTypeSection: return CLActivityType.count
case kDefaultNameSection: return 1
case kGPXFilesLocationSection: return 1
case kScreenSection: return 1
default: fatalError("Unknown section")
}
}
/// For `kCacheSection`:
/// 1. If `indexPath.row` is equal to `kUserOfflineCacheCell`, returns a cell with a checkmark
/// 2. If `indexPath.row` is equal to `kClearCacheCell`, returns a cell with a red text
/// `kClearCacheCell`
///
/// If the section is kMapSourceSection, it returns a chekmark cell with the name of
/// the tile server in the `indexPath.row` index in `GPXTileServer`. The cell is marked
/// if `selectedTileServerInt` is the same as `indexPath.row`.
///
/// If the section is kActivityTypeSection it returns a checkmark cell with the name
/// and description of the CLActivityType whose indexPath.row matches with the activity type.
///
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell(style: .value1, reuseIdentifier: "MapCell")
// Units section
if indexPath.section == kUnitsSection {
switch indexPath.row {
case kUseImperialUnitsCell:
cell = UITableViewCell(style: .value1, reuseIdentifier: "CacheCell")
cell.textLabel?.text = NSLocalizedString("USE_IMPERIAL_UNITS", comment: "no comment")
if preferences.useImperial {
cell.accessoryType = .checkmark
}
default: fatalError("Unknown section")
}
}
// Units section
if indexPath.section == kScreenSection {
switch indexPath.row {
case kKeepScreenAlwaysOnCell:
cell = UITableViewCell(style: .value1, reuseIdentifier: "CacheCell")
cell.textLabel?.text = NSLocalizedString("KEEP_SCREEN_ALWAYS_ON", comment: "no comment")
if preferences.keepScreenAlwaysOn {
cell.accessoryType = .checkmark
}
default: fatalError("Unknown section")
}
}
// Cache Section
if indexPath.section == kCacheSection {
switch indexPath.row {
case kUseOfflineCacheCell:
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "CacheCell")
cell.textLabel?.text = NSLocalizedString("OFFLINE_CACHE", comment: "no comment")
cell.detailTextLabel?.text = cachedSize
if preferences.useCache {
cell.accessoryType = .checkmark
}
case kClearCacheCell:
cell = UITableViewCell(style: .value1, reuseIdentifier: "CacheCell")
cell.textLabel?.text = NSLocalizedString("CLEAR_CACHE", comment: "no comment")
cell.textLabel?.textColor = UIColor.red
default: fatalError("Unknown section")
}
}
// Map Section
if indexPath.section == kMapSourceSection {
let tileServer = GPXTileServer(rawValue: indexPath.row)
cell.textLabel?.text = tileServer!.name
if indexPath.row == preferences.tileServerInt {
cell.accessoryType = .checkmark
}
}
// Activity type section
if indexPath.section == kActivityTypeSection {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "ActivityCell")
let activity = CLActivityType(rawValue: indexPath.row + 1)!
cell.textLabel?.text = activity.name
cell.detailTextLabel?.text = activity.description
if indexPath.row + 1 == preferences.locationActivityTypeInt {
cell.accessoryType = .checkmark
}
}
// Default Name section
if indexPath.section == kDefaultNameSection {
let dateFormatter = DefaultDateFormat()
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "DefaultNameCell")
cell.textLabel?.text = preferences.dateFormatPreset == -1 ? preferences.dateFormatInput : preferences.dateFormatPresetName
let dateText = dateFormatter.getDate(processedFormat: preferences.dateFormat,
useUTC: preferences.dateFormatUseUTC,
useENLocale: preferences.dateFormatUseEN)
cell.detailTextLabel?.text = dateText
cell.accessoryType = .disclosureIndicator
}
if indexPath.section == kGPXFilesLocationSection {
cell = UITableViewCell(style: .value1, reuseIdentifier: "GPXFilesLocation")
if let url = preferences.gpxFilesFolderURL {
cell.textLabel?.text = url.lastPathComponent
} else {
cell.textLabel?.text = NSLocalizedString("USING_DEFAULT_FOLDER", comment: "no comment")
}
cell.accessoryType = .disclosureIndicator
}
return cell
}
/// Performs the following actions depending on the section and row selected:
/// If the cell `kUseImperialUnitCell` in `kUnitsSection`it sets or unsets the use of imperial
/// units (`useImperial` in `Preferences``and calls the delegate method `didUpdateUseImperial`.
///
/// If a cell in kCacheSection is selected and the cell is
/// 1. kUseOfflineCacheCell: Activates or desactivates the `useCache` in `Preferences`,
/// and calls the delegate method `didUpdateUseCache`
/// 2. KClearCacheCacheCell: Clears the current cache and calls
///
/// If a cell in `kMapSourceSection` is selected: Updates `tileServerInt` in `Preferences` and
/// calls the delegate method `didUpdateTileServer`
///
/// If a cell in `kActivitySection` is selected: Updates the `activityType` in `Preferences` and
/// calls the delegate method `didUpdateActivityType`.
///
/// In each case checks or unchecks the corresponding cell in the UI.
///
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == kUnitsSection {
switch indexPath.row {
case kUseImperialUnitsCell:
let newUseImperial = !preferences.useImperial
preferences.useImperial = newUseImperial
print("PreferencesTableViewController: toggle imperial units to \(newUseImperial)")
// Update cell UI
tableView.cellForRow(at: indexPath)?.accessoryType = newUseImperial ? .checkmark : .none
// Notify the map
self.delegate?.didUpdateUseImperial(newUseImperial)
default:
fatalError("didSelectRowAt: Unknown cell")
}
}
if indexPath.section == kScreenSection {
switch indexPath.row {
case kKeepScreenAlwaysOnCell:
let newKeepScreenAlwaysOn = !preferences.keepScreenAlwaysOn
preferences.keepScreenAlwaysOn = newKeepScreenAlwaysOn
print("PreferencesTableViewController: toggle keep screen always on to \(newKeepScreenAlwaysOn)")
// Update cell UI
tableView.cellForRow(at: indexPath)?.accessoryType = newKeepScreenAlwaysOn ? .checkmark : .none
// Notify the map
self.delegate?.didUpdateKeepScreenAlwaysOn(newKeepScreenAlwaysOn)
default:
fatalError("didSelectRowAt: Unknown cell")
}
}
if indexPath.section == kCacheSection { // 0 -> sets and unsets cache
switch indexPath.row {
case kUseOfflineCacheCell:
print("toggle cache")
let newUseCache = !preferences.useCache // Toggle value
preferences.useCache = newUseCache
// Update cell
tableView.cellForRow(at: indexPath)?.accessoryType = newUseCache ? .checkmark : .none
// Notify the map
self.delegate?.didUpdateUseCache(newUseCache)
case kClearCacheCell:
print("clear cache")
// Create a cache
cache.clear {
print("Cache cleaned")
let cell = tableView.cellForRow(at: indexPath)!
cell.textLabel?.text = NSLocalizedString("CACHE_IS_EMPTY", comment: "no comment")
cell.textLabel?.textColor = UIColor.gray
// Clear the size text
let cell2 = tableView.cellForRow(at: IndexPath(row: kUseOfflineCacheCell, section: kCacheSection))
self.cachedSize = 0.asFileSize()
cell2?.detailTextLabel?.text = self.cachedSize
}
default:
fatalError("didSelectRowAt: Unknown cell")
}
}
if indexPath.section == kMapSourceSection { // section 1 (sets tileServerInt in defaults
print("PreferenccesTableView Map Tile Server section Row at index: \(indexPath.row)")
// Remove checkmark from selected tile server
let selectedTileServerIndexPath = IndexPath(row: preferences.tileServerInt, section: indexPath.section)
tableView.cellForRow(at: selectedTileServerIndexPath)?.accessoryType = .none
// Add checkmark to new tile server
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
preferences.tileServerInt = indexPath.row
// Update map
self.delegate?.didUpdateTileServer((indexPath as NSIndexPath).row)
}
if indexPath.section == kActivityTypeSection {
print("PreferencesTableView Activity Type section Row at index: \(indexPath.row + 1)")
let selected = IndexPath(row: preferences.locationActivityTypeInt - 1, section: indexPath.section)
tableView.cellForRow(at: selected)?.accessoryType = .none
// Add checkmark to new tile server
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
preferences.locationActivityTypeInt = indexPath.row + 1 // +1 as activityType raw value starts at index 1
self.delegate?.didUpdateActivityType((indexPath as NSIndexPath).row + 1)
}
if indexPath.section == kDefaultNameSection {
print("PreferencesTableView Default Name cell clicked")
self.navigationController?.pushViewController(DefaultNameSetupViewController(style: .grouped), animated: true)
}
if indexPath.section == kGPXFilesLocationSection {
print("PreferencesTableView GPX Files Location cell clicked")
let documentVC = UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String], in: .open)
documentVC.allowsMultipleSelection = false
documentVC.delegate = self
self.present(documentVC, animated: true)
}
// Unselect row
tableView.deselectRow(at: indexPath, animated: true)
}
// MARK: - UIDocumentPickerDelegate
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let folderURL = urls.first else {
print("Didn't select any folder")
return
}
preferences.gpxFilesFolderURL = folderURL
tableView.reloadData()
}
}