Signal-iOS/Signal/src/ViewControllers/DebugUI/LogViewController.swift
2019-09-13 10:31:17 -07:00

155 lines
5.0 KiB
Swift

//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
public class LogPickerViewController: OWSTableViewController {
let logDirUrl: URL
@objc
public init(logDirUrl: URL) {
self.logDirUrl = logDirUrl
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func viewDidLoad() {
super.viewDidLoad()
updateTableContents()
}
public func updateTableContents() {
let contents = OWSTableContents()
contents.addSection(buildPreferenceSection())
contents.addSection(buildLogsSection())
self.contents = contents
}
private func buildPreferenceSection() -> OWSTableSection {
let enableItem = OWSTableItem.switch(withText: "🚂 Play Sound When Errors Occur",
isOn: { OWSPreferences.isAudibleErrorLoggingEnabled() },
target: self,
selector: #selector(didToggleAudiblePreference(_:)))
return OWSTableSection(title: "Preferences", items: [enableItem])
}
private func buildLogsSection() -> OWSTableSection {
guard let directoryEnumerator = FileManager.default.enumerator(at: logDirUrl, includingPropertiesForKeys: nil) else {
owsFailDebug("logUrls was unexpectedly nil")
return OWSTableSection(title: "No Log URLs", items: [])
}
let logUrls: [URL] = directoryEnumerator.compactMap { $0 as? URL }
let sortedUrls = logUrls.sorted { (a, b) -> Bool in
return a.lastPathComponent > b.lastPathComponent
}
let logItems: [OWSTableItem] = sortedUrls.map { logUrl in
return OWSTableItem(
customCellBlock: { () -> UITableViewCell in
let cell = OWSTableItem.newCell()
guard let textLabel = cell.textLabel else {
owsFailDebug("textLabel was unexpectedly nil")
return cell
}
textLabel.lineBreakMode = .byTruncatingHead
textLabel.text = logUrl.lastPathComponent
return cell
},
actionBlock: { [weak self] in
guard let self = self else { return }
let logVC = LogViewController(logUrl: logUrl)
guard let navigationController = self.navigationController else {
owsFailDebug("navigationController was unexpectedly nil")
return
}
navigationController.pushViewController(logVC, animated: true)
}
)
}
return OWSTableSection(title: "View Logs", items: logItems)
}
@objc
func didToggleAudiblePreference(_ sender: UISwitch) {
OWSPreferences.setIsAudibleErrorLoggingEnabled(sender.isOn)
if sender.isOn {
ErrorLogger.playAlertSound()
}
}
}
@objc
public class LogViewController: UIViewController {
let logUrl: URL
@objc
public init(logUrl: URL) {
self.logUrl = logUrl
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let textView = UITextView()
override public func loadView() {
self.view = textView
loadLogText()
}
override public func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItems = [UIBarButtonItem(barButtonSystemItem: .action,
target: self,
action: #selector(didTapShare(_:))),
UIBarButtonItem(barButtonSystemItem: .trash,
target: self,
action: #selector(didTapTrash(_:)))]
}
func loadLogText() {
do {
// This is super crude, but:
// 1. generally we should haven't a ton of logged errors
// 2. this is a dev tool
let logData = try Data(contentsOf: logUrl)
// TODO most recent lines on top?
textView.text = String(data: logData, encoding: .utf8)
} catch {
textView.text = "Failed to load log data: \(error)"
}
}
@objc
func didTapTrash(_ sender: UIBarButtonItem) {
// truncate logUrl
do {
try NSData().write(to: logUrl)
loadLogText()
} catch {
owsFailDebug("error: \(error)")
}
}
@objc
func didTapShare(_ sender: UIBarButtonItem) {
let logText = textView.text ?? "Empty Log"
let vc = UIActivityViewController(activityItems: [logText], applicationActivities: [])
present(vc, animated: true)
}
}