-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathStatus.swift
More file actions
116 lines (102 loc) · 3.65 KB
/
Status.swift
File metadata and controls
116 lines (102 loc) · 3.65 KB
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
import AppKit
import Foundation
public enum ExtensionPermissionStatus {
case unknown
case succeeded
case failed
}
@objc public enum ObservedAXStatus: Int {
case unknown = -1
case granted = 1
case notGranted = 0
}
public extension Notification.Name {
static let serviceStatusDidChange = Notification.Name("com.github.CopilotForXcode.serviceStatusDidChange")
}
public struct StatusResponse {
public let icon: String
public let system: Bool // Temporary workaround for status images
public let message: String?
public let url: String?
}
public final actor Status {
public static let shared = Status()
private var extensionStatus: ExtensionPermissionStatus = .unknown
private var axStatus: ObservedAXStatus = .unknown
private init() {}
public func updateExtensionStatus(_ status: ExtensionPermissionStatus) {
guard status != extensionStatus else { return }
extensionStatus = status
broadcast()
}
public func updateAXStatus(_ status: ObservedAXStatus) {
guard status != axStatus else { return }
axStatus = status
broadcast()
}
public func getAXStatus() -> ObservedAXStatus {
// if Xcode is running, return the observed status
if isXcodeRunning() {
return axStatus
} else if AXIsProcessTrusted() {
// if Xcode is not running but AXIsProcessTrusted() is true, return granted
return .granted
} else {
// otherwise, return the last observed status, which may be unknown
return axStatus
}
}
private func isXcodeRunning() -> Bool {
let xcode = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dt.Xcode")
return !xcode.isEmpty
}
public func getStatus() -> StatusResponse {
if extensionStatus == .failed {
// TODO differentiate between the permission not being granted and the
// extension just getting disabled by Xcode.
return .init(
icon: "exclamationmark.circle",
system: true,
message: """
Extension is not enabled. Enable GitHub Copilot under Xcode
and then restart Xcode.
""",
url: "x-apple.systempreferences:com.apple.ExtensionsPreferences"
)
}
switch getAXStatus() {
case .granted:
return .init(icon: "MenuBarIcon", system: false, message: nil, url: nil)
case .notGranted:
return .init(
icon: "exclamationmark.circle",
system: true,
message: """
Accessibility permission not granted. \
Click to open System Preferences.
""",
url: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
)
case .unknown:
return .init(
icon: "exclamationmark.circle",
system: true,
message: """
Accessibility permission not granted or Copilot restart needed.
""",
url: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
)
}
}
private func broadcast() {
NotificationCenter.default.post(
name: .serviceStatusDidChange,
object: nil
)
// Can remove DistributedNotificationCenter if the settings UI moves in-process
DistributedNotificationCenter.default().post(
name: .serviceStatusDidChange,
object: nil
)
}
}