Compare commits

...

6 Commits

Author SHA1 Message Date
Duraid Abdul d995119198 Update LCManager.swift 2022-02-01 12:26:52 -08:00
Duraid Abdul b4d7c06432 Update LCManager.swift 2022-01-26 21:02:54 -08:00
Duraid Abdul a7b95a4379 Update LCManager.swift 2022-01-26 02:18:26 -08:00
Duraid Abdul 3efe25f804 Update LCManager.swift 2022-01-25 17:15:30 -08:00
Duraid Abdul 0a3e28b28f Animation Improvements 2022-01-24 23:09:49 -08:00
Duraid Abdul 009fab95be UserDefaults Support
Signed-off-by: Duraid Abdul <duraidabdul@users.noreply.github.com>
2022-01-24 19:06:49 -08:00
+239 -54
View File
@@ -82,8 +82,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.startAnimation()
grabberMode = false
UserDefaults.standard.set(consoleView.center.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(consoleView.center.y, forKey: "LocalConsole_Y")
UserDefaults.standard.set(consoleView.center.x, forKey: "LocalConsole.X")
UserDefaults.standard.set(consoleView.center.y, forKey: "LocalConsole.Y")
}), for: .touchUpInside)
consoleView.addSubview(button)
@@ -131,8 +131,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
// TODO: Snap to nearest position.
UserDefaults.standard.set(consoleSize.width, forKey: "LocalConsole_Width")
UserDefaults.standard.set(consoleSize.height, forKey: "LocalConsole_Height")
UserDefaults.standard.set(consoleSize.width, forKey: "LocalConsole.Width")
UserDefaults.standard.set(consoleSize.height, forKey: "LocalConsole.Height")
}
}
@@ -267,8 +267,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lazy var initialViewLocation: CGPoint = .zero
func configureConsole() {
consoleSize = CGSize(width: UserDefaults.standard.object(forKey: "LocalConsole_Width") as? CGFloat ?? consoleSize.width,
height: UserDefaults.standard.object(forKey: "LocalConsole_Height") as? CGFloat ?? consoleSize.height)
consoleSize = CGSize(width: UserDefaults.standard.object(forKey: "LocalConsole.Width") as? CGFloat ?? consoleSize.width,
height: UserDefaults.standard.object(forKey: "LocalConsole.Height") as? CGFloat ?? consoleSize.height)
consoleView.layer.shadowRadius = 16
@@ -312,7 +312,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let tapRecognizer = UITapStartEndGestureRecognizer(target: self, action: #selector(consolePiPTapStartEnd(recognizer:)))
tapRecognizer.delegate = self
longPressRecognizer.minimumPressDuration = 0.1
longPressRecognizer.minimumPressDuration = 0.3
consoleView.addGestureRecognizer(panRecognizer)
consoleView.addGestureRecognizer(tapRecognizer)
@@ -383,9 +383,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
.first
if let windowScene = windowScene as? UIWindowScene {
windowSceneFound = true
UIWindow.swizzleStatusBarAppearanceOverride()
SwizzleTool().swizzleContextMenuReverseOrder()
consoleWindow = ConsoleWindow(windowScene: windowScene)
consoleWindow?.frame = UIScreen.main.bounds
consoleWindow?.windowLevel = UIWindow.Level.statusBar
@@ -397,8 +399,6 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
viewController.view.addSubview(consoleView)
UIWindow.swizzleStatusBarAppearanceOverride
updateConsoleOrigin()
}
}
@@ -424,8 +424,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
func snapToCachedEndpoint() {
let cachedConsolePosition = CGPoint(x: UserDefaults.standard.object(forKey: "LocalConsole_X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole_Y") as? CGFloat ?? possibleEndpoints.first!.y)
let cachedConsolePosition = CGPoint(x: UserDefaults.standard.object(forKey: "LocalConsole.X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole.Y") as? CGFloat ?? possibleEndpoints.first!.y)
consoleView.center = cachedConsolePosition // Update console center so possibleEndpoints are calculated correctly.
consoleView.center = nearestTargetTo(cachedConsolePosition, possibleTargets: possibleEndpoints)
@@ -637,15 +637,23 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
var dynamicReportTimer: Timer? {
willSet { dynamicReportTimer?.invalidate() }
willSet {
timerInvalidationCounter = 0
dynamicReportTimer?.invalidate()
}
}
var timerInvalidationCounter = 0
func systemReport() {
DispatchQueue.main.async { [self] in
if currentText != "" { print("\n") }
dynamicReportTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
guard consoleTextView.panGestureRecognizer.numberOfTouches == 0 else { return }
var _currentText = currentText
// To optimize performance, only scan the last 2500 characters of text for system report changes.
@@ -667,10 +675,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if currentText != _currentText {
currentText = _currentText
timerInvalidationCounter = 0
} else {
// Invalidate the timer if there is no longer anything to update.
timer.invalidate()
timerInvalidationCounter += 1
// It has been 2 seconds and values have not changed.
if timerInvalidationCounter == 2 {
// Invalidate the timer if there is no longer anything to update.
dynamicReportTimer = nil
}
}
}
@@ -742,11 +759,16 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleTextView.attributedText = NSAttributedString(string: string, attributes: attributes)
}
// Displays all UserDefaults keys, including unneeded keys that are included by default.
public var showAllUserDefaultsKeys = false
func makeMenu() -> UIMenu {
let copy = UIAction(title: "Copy",
image: UIImage(systemName: "doc.on.doc"), handler: { _ in
self.copy()
let share = UIAction(title: "Share Text...",
image: UIImage(systemName: "square.and.arrow.up"), handler: { _ in
let activityViewController = UIActivityViewController(activityItems: [self.consoleTextView.text ?? ""],
applicationActivities: nil)
self.viewController.present(activityViewController, animated: true)
})
let resize = UIAction(title: "Resize Console",
@@ -773,10 +795,127 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let consoleActions = UIMenu(title: "", options: .displayInline, children: [clear, resize])
var frameSymbol = "rectangle.3.offgrid"
var debugActions: [UIMenuElement] = []
if #available(iOS 15, *) {
frameSymbol = "square.inset.filled"
let deferredUserDefaultsList = UIDeferredMenuElement.uncached { completion in
var actions: [UIAction] = []
let keys: [String] = {
if self.showAllUserDefaultsKeys {
return UserDefaults.standard.dictionaryRepresentation().map { $0.key }
}
// Show keys the developer has added to the app (+ LocalConsole keys), excluding all of Apple's keys.
if let bundle: String = Bundle.main.bundleIdentifier {
let preferencePath: String = NSHomeDirectory() + "/Library/Preferences/\(bundle).plist"
let _keys = NSDictionary(contentsOfFile: preferencePath)?.allKeys as! [String]
return _keys.filter {
!$0.contains("LocalConsole.")
}
}
return []
}()
if keys.isEmpty {
actions.append(UIAction(title: "No Entries",
image: nil, attributes: .disabled, handler: { _ in }
))
} else {
for key in keys.sorted(by: { $0.lowercased() < $1.lowercased() }) {
// Old LocalConsole Key Cleanup
guard !key.contains("LocalConsole_") else {
UserDefaults.standard.removeObject(forKey: key)
continue
}
if let value = UserDefaults.standard.value(forKey: key) {
let action = UIAction(title: key, image: nil) { _ in
let alertController = UIAlertController(title: key,
message: nil,
preferredStyle: .alert)
let headerParagraphStyle = NSMutableParagraphStyle()
headerParagraphStyle.paragraphSpacing = 6
let contentParagraphStyle = NSMutableParagraphStyle()
let attributes: [NSAttributedString.Key: Any] = [
.paragraphStyle: contentParagraphStyle,
.foregroundColor: UIColor.label,
.font: UIFont.systemFont(ofSize: 13, weight: .semibold, design: .monospaced)
]
let attributedTitle: NSMutableAttributedString = {
let attributedString = NSMutableAttributedString(string: "Key\n" + key, attributes: attributes)
attributedString.addAttributes(
[NSAttributedString.Key.foregroundColor : UIColor.label.withAlphaComponent(0.5),
NSAttributedString.Key.paragraphStyle : headerParagraphStyle],
range: NSRange(location: 0, length: 3))
return attributedString
}()
let attributedMessage: NSMutableAttributedString = {
let attributedString = NSMutableAttributedString(string: "\nValue\n" + "\(value)", attributes: attributes)
attributedString.addAttributes(
[NSAttributedString.Key.foregroundColor : UIColor.label.withAlphaComponent(0.5),
NSAttributedString.Key.paragraphStyle : headerParagraphStyle],
range: NSRange(location: 0, length: 7))
return attributedString
}()
alertController.setValue(attributedTitle, forKey: "attributedTitle")
alertController.setValue(attributedMessage, forKey: "attributedMessage")
alertController.addAction(UIAlertAction(title: "Copy Value", style: .default, handler: { _ in
UIPasteboard.general.string = "\(value)"
}))
alertController.addAction(UIAlertAction(title: "Clear Value", style: .destructive, handler: { _ in
UserDefaults.standard.removeObject(forKey: key)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
}))
self.viewController.present(alertController,
animated: true)
}
action.subtitle = "\(value)"
actions.append(action)
}
}
actions.append(
UIAction(title: "Clear Defaults",
image: UIImage(systemName: "trash"), attributes: .destructive, handler: { _ in
keys.forEach {
UserDefaults.standard.removeObject(forKey: $0)
}
})
)
}
completion(actions)
}
let userDefaults = UIMenu(title: "UserDefaults", image: UIImage(systemName: "doc.badge.gearshape"), children: [deferredUserDefaultsList])
debugActions.append(userDefaults)
}
let viewFrames = UIAction(title: debugBordersEnabled ? "Hide View Frames" : "Show View Frames",
image: UIImage(systemName: frameSymbol), handler: { _ in
self.debugBordersEnabled.toggle()
@@ -840,22 +979,26 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
})
let debugActions = UIMenu(title: "", options: .displayInline,
debugActions.append(contentsOf: [viewFrames, systemReport, displayReport, respring])
let debugMenu = UIMenu(title: "", options: .displayInline,
children: [UIMenu(title: "Debug", image: UIImage(systemName: "ant"),
children: [viewFrames, systemReport, displayReport, respring])])
children: debugActions)])
var menuContent: [UIMenuElement] = []
if consoleTextView.text != "" {
menuContent.append(contentsOf: [copy, consoleActions])
menuContent.append(contentsOf: [share, consoleActions])
} else {
menuContent.append(resize)
}
menuContent.append(debugActions)
menuContent.append(debugMenu)
return UIMenu(title: "", children: menuContent)
}
var consolePiPPopAnimator: UIViewPropertyAnimator?
@objc func longPressAction(recognizer: UILongPressGestureRecognizer) {
switch recognizer.state {
case .began:
@@ -866,8 +1009,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
scrollLocked = false
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consolePiPPopAnimator = UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 1.04, y: 1.04)
}
consolePiPPopAnimator?.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleTextView.alpha = 0.5
menuButton.alpha = 0.5
}.startAnimation()
@@ -875,7 +1022,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if !grabberMode { scrollLocked = true }
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.5) { [self] in
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.6) { [self] in
consoleView.transform = .identity
}.startAnimation()
@@ -902,7 +1049,10 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
initialViewLocation = consoleView.center
}
guard !scrollLocked else { return }
guard !scrollLocked else {
isPressed = false
return
}
let translation = recognizer.translation(in: consoleView.superview)
let velocity = recognizer.velocity(in: consoleView.superview)
@@ -948,8 +1098,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
positionAnimator.startAnimation()
UserDefaults.standard.set(nearestTargetPosition.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(nearestTargetPosition.y, forKey: "LocalConsole_Y")
UserDefaults.standard.set(nearestTargetPosition.x, forKey: "LocalConsole.X")
UserDefaults.standard.set(nearestTargetPosition.y, forKey: "LocalConsole.Y")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.reassessGrabberMode()
@@ -968,29 +1118,37 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
// Animate touch down.
func consolePiPTouchDown() {
guard !grabberMode else { return }
UIViewPropertyAnimator(duration: 1.25, dampingRatio: 0.5) { [self] in
consoleView.transform = .init(scaleX: 0.95, y: 0.95)
}.startAnimation()
}
var consolePiPTouchDownAnimator: UIViewPropertyAnimator?
// Animate touch up.
func consolePiPTouchUp() {
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.4) { [self] in
consoleView.transform = .init(scaleX: 1, y: 1)
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
if !grabberMode {
consoleTextView.alpha = 1
if !ResizeController.shared.isActive {
menuButton.alpha = 1
var isPressed: Bool = false {
didSet {
guard oldValue != isPressed else { return }
if isPressed {
guard !grabberMode else { return }
consolePiPTouchDownAnimator = UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 0.96, y: 0.96)
}
consolePiPTouchDownAnimator?.startAnimation(afterDelay: 0.1)
} else {
consolePiPTouchDownAnimator?.stopAnimation(true)
consolePiPPopAnimator?.stopAnimation(true)
UIViewPropertyAnimator(duration: scrollLocked ? 0.4 : 0.7, dampingRatio: scrollLocked ? 1 : 0.45) { [self] in
consoleView.transform = .identity
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
if !grabberMode {
consoleTextView.alpha = 1
if !ResizeController.shared.isActive {
menuButton.alpha = 1
}
}
}.startAnimation()
}
}.startAnimation()
}
}
// Simulataneously listen to all gesture recognizers.
@@ -1001,11 +1159,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
@objc func consolePiPTapStartEnd(recognizer: UITapStartEndGestureRecognizer) {
switch recognizer.state {
case .began:
consolePiPTouchDown()
isPressed = true
case .changed:
break
case .ended, .cancelled, .possible, .failed:
consolePiPTouchUp()
isPressed = false
@unknown default:
break
}
@@ -1072,21 +1230,49 @@ extension UIView {
extension UIWindow {
/// Make sure this window does not have control over the status bar appearance.
static let swizzleStatusBarAppearanceOverride: Void = {
guard let originalMethod = class_getInstanceMethod(UIWindow.self, NSSelectorFromString("_can" + "Affect" + "Sta" + "tus" + "Bar" + "Appe" + "arance")),
static func swizzleStatusBarAppearanceOverride() {
guard let originalMethod = class_getInstanceMethod(UIWindow.self, NSSelectorFromString("_can" + "Affect" + "Status" + "Bar" + "Appearance")),
let swizzledMethod = class_getInstanceMethod(UIWindow.self, #selector(swizzled_statusBarAppearance))
else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}()
}
@objc func swizzled_statusBarAppearance() -> Bool {
return isKeyWindow
}
}
class SwizzleTool: NSObject {
/// Ensure context menus always show in a non reversed order.
func swizzleContextMenuReverseOrder() {
guard let originalMethod = class_getInstanceMethod(NSClassFromString("_" + "UI" + "Context" + "Menu" + "List" + "View").self, NSSelectorFromString("reverses" + "Action" + "Order")),
let swizzledMethod = class_getInstanceMethod(SwizzleTool.self, #selector(swizzled_reverses_Action_Order))
else { Swift.print("Swizzle Error Occurred"); return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@objc func swizzled_reverses_Action_Order() -> Bool {
if let menu = self.value(forKey: "displayed" + "Menu") as? UIMenu,
menu.title == "Debug" || menu.title == "User" + "Defaults" {
return false
}
if let orig = self.value(forKey: "_" + "reverses" + "Action" + "Order") as? Bool {
return orig
}
return false
}
}
class LumaView: UIView {
lazy var visualEffectView: UIView = {
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Frameworks/Material" + "Kit." + "framework")!.load()
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Framework" + "s/Material" + "Kit." + "framework")!.load()
let Pill = NSClassFromString("MT" + "Luma" + "Dodge" + "Pill" + "View") as! UIView.Type
@@ -1218,7 +1404,6 @@ fileprivate func _debugPrint(_ items: Any) {
// Support for auto-rotate.
class ConsoleViewController: UIViewController {
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Cancel the panner console is being panned to allow for location manipulation.