Adds “Export Error Log…” button to Settings
Also hides “Experimental Features” setting for public builds
This commit is contained in:
parent
a8b9bd1092
commit
4b159603a1
@ -19,7 +19,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="separatorColor" systemColor="separatorColor"/>
|
||||
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delta 0.6.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Str-BY-agW">
|
||||
<rect key="frame" x="0.0" y="2160" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="2204" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
@ -505,11 +505,38 @@
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Advanced" footerTitle="Test out new features that have been added by contributors." id="EzW-IN-YAf" userLabel="Experimental Features">
|
||||
<tableViewSection headerTitle="Advanced" footerTitle="Test out new features that have been added by contributors." id="EzW-IN-YAf">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Hl1-if-S1K" style="IBUITableViewCellStyleDefault" id="8fe-ab-zkf">
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="ActivityCell" id="0bj-he-6br">
|
||||
<rect key="frame" x="16" y="1570.5" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="0bj-he-6br" id="oIP-D5-Xrm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Export Error Log…" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XPB-zq-ssX">
|
||||
<rect key="frame" x="16" y="11.5" width="138" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" name="Purple"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" misplaced="YES" hidesWhenStopped="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="x5w-dE-ucW">
|
||||
<rect key="frame" x="307" y="12" width="20" height="21"/>
|
||||
<color key="color" name="Purple"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="x5w-dE-ucW" firstAttribute="centerY" secondItem="oIP-D5-Xrm" secondAttribute="centerY" id="2Du-al-MgG"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="x5w-dE-ucW" secondAttribute="trailing" id="7ek-gm-AM9"/>
|
||||
<constraint firstItem="XPB-zq-ssX" firstAttribute="centerY" secondItem="oIP-D5-Xrm" secondAttribute="centerY" id="HsH-e7-iRR"/>
|
||||
<constraint firstItem="XPB-zq-ssX" firstAttribute="leading" secondItem="oIP-D5-Xrm" secondAttribute="leadingMargin" id="Ulm-U3-OcK"/>
|
||||
<constraint firstItem="x5w-dE-ucW" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="XPB-zq-ssX" secondAttribute="trailing" constant="8" symbolic="YES" id="vfa-y3-UZc"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Hl1-if-S1K" style="IBUITableViewCellStyleDefault" id="8fe-ab-zkf">
|
||||
<rect key="frame" x="16" y="1614.5" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8fe-ab-zkf" id="Py0-GQ-Z36">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@ -528,7 +555,7 @@
|
||||
<tableViewSection headerTitle="Patreon" footerTitle="Receive early access to new features and more by becoming a patron." id="QvT-Yt-oP1">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="faT-qa-JP0" style="IBUITableViewCellStyleDefault" id="4it-3L-j8P">
|
||||
<rect key="frame" x="16" y="1706" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="1750" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4it-3L-j8P" id="7dE-36-hzp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -548,7 +575,7 @@
|
||||
<tableViewSection headerTitle="Credits" id="foh-L9-g6W">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="Pum-dL-hGn" detailTextLabel="WQ6-m7-zhh" style="IBUITableViewCellStyleValue1" id="BU4-ee-DGz">
|
||||
<rect key="frame" x="16" y="1834" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="1878" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="BU4-ee-DGz" id="fWf-gm-1sf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -571,7 +598,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="cht-lO-kpR" detailTextLabel="0pG-CT-ZWR" style="IBUITableViewCellStyleValue1" id="CV9-Df-mUX">
|
||||
<rect key="frame" x="16" y="1878" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="1922" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="CV9-Df-mUX" id="gLC-z2-rMU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -594,7 +621,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="gWx-Xn-5Nf" detailTextLabel="09x-GX-cpy" style="IBUITableViewCellStyleValue1" id="8qc-0t-Nte">
|
||||
<rect key="frame" x="16" y="1922" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="1966" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8qc-0t-Nte" id="jUL-fL-i0n">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -617,7 +644,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="zro-BX-EY9" detailTextLabel="e45-FD-ug2" style="IBUITableViewCellStyleValue1" id="Zh9-JJ-jEQ">
|
||||
<rect key="frame" x="16" y="1966" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="2010" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Zh9-JJ-jEQ" id="VCc-oJ-ODB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -640,7 +667,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="w1i-mR-wOF" detailTextLabel="jRO-48-iRO" style="IBUITableViewCellStyleValue1" id="rrX-Bh-zdW">
|
||||
<rect key="frame" x="16" y="2010" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="2054" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="rrX-Bh-zdW" id="b9U-W6-LnS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -663,7 +690,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="g59-8E-zW7" style="IBUITableViewCellStyleDefault" id="hkv-lx-68h">
|
||||
<rect key="frame" x="16" y="2054" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="2098" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hkv-lx-68h" id="bNT-kB-3cI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -679,7 +706,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="2K3-IL-94S" style="IBUITableViewCellStyleDefault" id="j7p-ZK-mHq">
|
||||
<rect key="frame" x="16" y="2098" width="343" height="44"/>
|
||||
<rect key="frame" x="16" y="2142" width="343" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="j7p-ZK-mHq" id="BqT-yP-OpS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316.5" height="44"/>
|
||||
@ -717,6 +744,7 @@
|
||||
<outlet property="buttonHapticFeedbackEnabledSwitch" destination="g7m-wj-ueP" id="nOj-sc-foi"/>
|
||||
<outlet property="controllerOpacityLabel" destination="zaz-yD-CYG" id="eUW-u9-xxx"/>
|
||||
<outlet property="controllerOpacitySlider" destination="whi-If-wFf" id="6Cx-HY-xLG"/>
|
||||
<outlet property="exportLogActivityIndicatorView" destination="x5w-dE-ucW" id="u5a-K3-2ag"/>
|
||||
<outlet property="previewsEnabledSwitch" destination="OJE-9e-9i3" id="Ndg-eN-PPs"/>
|
||||
<outlet property="respectSilentModeSwitch" destination="mlO-iy-zU2" id="TDT-cx-kCf"/>
|
||||
<outlet property="syncingServiceLabel" destination="kLY-5g-v8n" id="zzx-qM-q1g"/>
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import QuickLook
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
import Roxas
|
||||
|
||||
@ -43,6 +45,12 @@ private extension SettingsViewController
|
||||
case status
|
||||
}
|
||||
|
||||
enum AdvancedRow: Int, CaseIterable
|
||||
{
|
||||
case exportLog
|
||||
case experimentalFeatures
|
||||
}
|
||||
|
||||
enum CreditsRow: Int, CaseIterable
|
||||
{
|
||||
case riley
|
||||
@ -68,6 +76,7 @@ class SettingsViewController: UITableViewController
|
||||
@IBOutlet private var versionLabel: UILabel!
|
||||
|
||||
@IBOutlet private var syncingServiceLabel: UILabel!
|
||||
@IBOutlet private var exportLogActivityIndicatorView: UIActivityIndicatorView!
|
||||
|
||||
private var selectionFeedbackGenerator: UISelectionFeedbackGenerator?
|
||||
|
||||
@ -75,6 +84,8 @@ class SettingsViewController: UITableViewController
|
||||
|
||||
private var syncingConflictsCount = 0
|
||||
|
||||
private var _exportedLogURL: URL?
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
{
|
||||
super.init(coder: aDecoder)
|
||||
@ -196,6 +207,15 @@ private extension SettingsViewController
|
||||
{
|
||||
switch section
|
||||
{
|
||||
case .advanced:
|
||||
guard #unavailable(iOS 15) else { return false }
|
||||
|
||||
#if BETA
|
||||
return false
|
||||
#else
|
||||
return true
|
||||
#endif
|
||||
|
||||
case .hapticTouch:
|
||||
if #available(iOS 13, *)
|
||||
{
|
||||
@ -294,6 +314,55 @@ private extension SettingsViewController
|
||||
let hostingController = ExperimentalFeaturesView.makeViewController()
|
||||
self.navigationController?.pushViewController(hostingController, animated: true)
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
func exportErrorLog()
|
||||
{
|
||||
self.exportLogActivityIndicatorView.startAnimating()
|
||||
|
||||
if let indexPath = self.tableView.indexPathForSelectedRow
|
||||
{
|
||||
self.tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
Task<Void, Never>.detached(priority: .userInitiated) {
|
||||
do
|
||||
{
|
||||
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
|
||||
// All logs since the app launched.
|
||||
let position = store.position(timeIntervalSinceLatestBoot: 0)
|
||||
let predicate = NSPredicate(format: "subsystem IN %@", [Logger.deltaSubsystem, Logger.harmonySubsystem])
|
||||
|
||||
let entries = try store.getEntries(at: position, matching: predicate)
|
||||
.compactMap { $0 as? OSLogEntryLog }
|
||||
.map { "[\($0.date.formatted())] [\($0.category)] [\($0.level.localizedName)] \($0.composedMessage)" }
|
||||
|
||||
let outputText = entries.joined(separator: "\n")
|
||||
|
||||
let outputDirectory = FileManager.default.uniqueTemporaryURL()
|
||||
try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true)
|
||||
|
||||
let outputURL = outputDirectory.appendingPathComponent("delta.log")
|
||||
try outputText.write(to: outputURL, atomically: true, encoding: .utf8)
|
||||
|
||||
await MainActor.run {
|
||||
self._exportedLogURL = outputURL
|
||||
|
||||
let previewController = QLPreviewController()
|
||||
previewController.delegate = self
|
||||
previewController.dataSource = self
|
||||
self.present(previewController, animated: true)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to export Harmony logs.", error)
|
||||
}
|
||||
|
||||
await self.exportLogActivityIndicatorView.stopAnimating()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SettingsViewController
|
||||
@ -341,6 +410,9 @@ extension SettingsViewController
|
||||
case .controllers: return 4
|
||||
case .controllerSkins: return System.registeredSystems.count
|
||||
case .syncing: return SyncManager.shared.coordinator?.account == nil ? 1 : super.tableView(tableView, numberOfRowsInSection: sectionIndex)
|
||||
#if !BETA
|
||||
case .advanced: return 1
|
||||
#endif
|
||||
default:
|
||||
if isSectionHidden(section)
|
||||
{
|
||||
@ -410,7 +482,17 @@ extension SettingsViewController
|
||||
case .controllerSkins: self.performSegue(withIdentifier: Segue.controllerSkins.rawValue, sender: cell)
|
||||
case .cores: self.performSegue(withIdentifier: Segue.dsSettings.rawValue, sender: cell)
|
||||
case .controllerOpacity, .gameAudio, .hapticFeedback, .hapticTouch, .syncing: break
|
||||
case .advanced: self.showExperimentalFeatures()
|
||||
case .advanced:
|
||||
let row = AdvancedRow(rawValue: indexPath.row)!
|
||||
switch row
|
||||
{
|
||||
case .exportLog:
|
||||
guard #available(iOS 15, *) else { return }
|
||||
self.exportErrorLog()
|
||||
|
||||
case .experimentalFeatures: self.showExperimentalFeatures()
|
||||
}
|
||||
|
||||
case .patreon:
|
||||
let patreonURL = URL(string: "altstore://patreon")!
|
||||
|
||||
@ -449,6 +531,17 @@ extension SettingsViewController
|
||||
primary:
|
||||
switch Section(rawValue: indexPath.section)!
|
||||
{
|
||||
case .advanced:
|
||||
let row = AdvancedRow(rawValue: indexPath.row)!
|
||||
switch row
|
||||
{
|
||||
case .exportLog:
|
||||
guard #unavailable(iOS 15) else { break }
|
||||
return 0.0
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
case .credits:
|
||||
let row = CreditsRow(rawValue: indexPath.row)!
|
||||
switch row
|
||||
@ -492,14 +585,15 @@ extension SettingsViewController
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return nil }
|
||||
|
||||
if isSectionHidden(section)
|
||||
switch section
|
||||
{
|
||||
return nil
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||
#if !BETA
|
||||
case .advanced: return nil
|
||||
#endif
|
||||
|
||||
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,3 +625,26 @@ extension SettingsViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SettingsViewController: QLPreviewControllerDataSource, QLPreviewControllerDelegate
|
||||
{
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int
|
||||
{
|
||||
return 1
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem
|
||||
{
|
||||
return (_exportedLogURL as? NSURL) ?? NSURL()
|
||||
}
|
||||
|
||||
func previewControllerDidDismiss(_ controller: QLPreviewController)
|
||||
{
|
||||
guard let exportedLogURL = _exportedLogURL else { return }
|
||||
|
||||
let parentDirectory = exportedLogURL.deletingLastPathComponent()
|
||||
try? FileManager.default.removeItem(at: parentDirectory)
|
||||
|
||||
_exportedLogURL = nil
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user