Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.1.14 fixes #542

Merged
merged 6 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Nudge/UI/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var nudgeLogState = LogState()
struct Globals {
static let bundle = Bundle.main
static let bundleID = bundle.bundleIdentifier ?? "com.github.macadmins.Nudge"
static let dnc = DistributedNotificationCenter.default()
static let nc = NotificationCenter.default
static let snc = NSWorkspace.shared.notificationCenter
// Preferences
Expand Down
88 changes: 40 additions & 48 deletions Nudge/UI/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct ContentView: View {
.edgesIgnoringSafeArea(.all)
.onAppear {
initialLaunchLogic()
handleNudgeActivation()
updateUI()
}
.onReceive(Intervals.nudgeRefreshCycleTimer) { _ in
Expand All @@ -87,7 +88,6 @@ struct ContentView: View {
window?.isMovable = false
window?.collectionBehavior = [.fullScreenAuxiliary]
window?.delegate = UIConstants.windowDelegate
// _ = needToActivateNudge()
}

private func handleNudgeActivation() {
Expand Down Expand Up @@ -177,6 +177,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
handleCommandLineArguments()
applyGracePeriodLogic()
applyRandomDelayIfNecessary()
updateNudgeState()
handleSoftwareUpdateRequirements()
}

Expand Down Expand Up @@ -210,12 +211,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}

// Observe screen locking. Maybe useful later
@objc func screenLocked(_ notification: Notification) {
nudgePrimaryState.screenCurrentlyLocked = true
LogManager.info("Screen was locked", logger: utilsLog)
}

@objc func screenParametersChanged(_ notification: Notification) {
LogManager.info("Screen parameters changed - Notification Center", logger: utilsLog)
UIUtilities().centerNudge()
Expand All @@ -226,11 +221,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
UIUtilities().centerNudge()
}

@objc func screenUnlocked(_ notification: Notification) {
nudgePrimaryState.screenCurrentlyLocked = false
LogManager.info("Screen was unlocked", logger: utilsLog)
}

@objc func spacesStateChanged(_ notification: Notification) {
UIUtilities().centerNudge()
LogManager.info("Spaces state changed", logger: utilsLog)
Expand Down Expand Up @@ -291,9 +281,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private func detectBannedShortcutKeys(with event: NSEvent) -> Bool {
guard NSApplication.shared.isActive else { return false }
switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
// Disable CMD + W - closes the Nudge window and breaks it
case [.command] where event.charactersIgnoringModifiers == "w":
LogManager.warning("Nudge detected an attempt to close the application via CMD + W shortcut key.", logger: utilsLog)
// Disable CMD + H - Hides Nudge
case [.command] where event.charactersIgnoringModifiers == "h":
LogManager.warning("Nudge detected an attempt to hide the application via CMD + H shortcut key.", logger: utilsLog)
return true
// Disable CMD + M - Minimizes Nudge
case [.command] where event.charactersIgnoringModifiers == "m":
LogManager.warning("Nudge detected an attempt to minimize the application via CMD + M shortcut key.", logger: utilsLog)
return true
// Disable CMD + N - closes the Nudge window and breaks it
case [.command] where event.charactersIgnoringModifiers == "n":
Expand All @@ -303,26 +297,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
case [.command] where event.charactersIgnoringModifiers == "q":
LogManager.warning("Nudge detected an attempt to quit the application via CMD + Q shortcut key.", logger: utilsLog)
return true
// Disable CMD + M - Minimizes Nudge
case [.command] where event.charactersIgnoringModifiers == "m":
LogManager.warning("Nudge detected an attempt to minimize the application via CMD + M shortcut key.", logger: utilsLog)
return true
// Disable CMD + H - Hides Nudge
case [.command] where event.charactersIgnoringModifiers == "h":
LogManager.warning("Nudge detected an attempt to hide the application via CMD + H shortcut key.", logger: utilsLog)
return true
// Disable CMD + Option + Esc (Force Quit Applications)
case [.command, .option] where event.charactersIgnoringModifiers == "\u{1b}": // Escape key
LogManager.warning("Nudge detected an attempt to open Force Quit Applications via CMD + Option + Esc.", logger: utilsLog)
// Disable CMD + W - closes the Nudge window and breaks it
case [.command] where event.charactersIgnoringModifiers == "w":
LogManager.warning("Nudge detected an attempt to close the application via CMD + W shortcut key.", logger: utilsLog)
return true
// Disable CMD + Option + M - Minimizes Nudge
case [.command, .option] where event.charactersIgnoringModifiers == "µ":
case [.command, .option] where event.charactersIgnoringModifiers == "m":
LogManager.warning("Nudge detected an attempt to minimise the application via CMD + Option + M shortcut key.", logger: utilsLog)
return true
// Disable CMD + Option + N - Add tabs to Nudge window
case [.command, .option] where event.charactersIgnoringModifiers == "~":
case [.command, .option] where event.charactersIgnoringModifiers == "n":
LogManager.warning("Nudge detected an attempt to add tabs to the application via CMD + Option + N shortcut key.", logger: utilsLog)
return true
// Disable CMD + Option + W - Close Window
case [.command, .option] where event.charactersIgnoringModifiers == "w":
LogManager.warning("Nudge detected an attempt to add tabs to the application via CMD + Option + W shortcut key.", logger: utilsLog)
return true
// Disable CMD + Option + Esc (Force Quit Applications)
case [.command, .option] where event.charactersIgnoringModifiers == "\u{1b}": // Escape key
// This doesn't work since Apple allows that shortcut to bypass the application's memory.
LogManager.warning("Nudge detected an attempt to open Force Quit Applications via CMD + Option + Esc.", logger: utilsLog)
return true
default:
// Don't care about any other shortcut keys
return false
Expand All @@ -335,7 +330,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
if !nudgeLogState.afterFirstLaunch && OptionalFeatureVariables.terminateApplicationsOnLaunch {
terminateApplications()
}
Globals.nc.addObserver(
Globals.snc.addObserver(
self,
selector: #selector(terminateApplicationSender(_:)),
name: NSWorkspace.didLaunchApplicationNotification,
Expand Down Expand Up @@ -446,8 +441,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {

private func setupNotificationObservers() {
setupNotificationCenterObservers()
setupScreenLockObservers()
setupScreenChangeObservers()
setupScreenLockObservers()
setupWorkspaceNotificationCenterObservers()
}

Expand Down Expand Up @@ -478,19 +473,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

private func setupScreenLockObservers() {
Globals.nc.addObserver(
self,
selector: #selector(screenLocked),
name: NSNotification.Name("com.apple.screenIsLocked"),
object: nil
)

Globals.nc.addObserver(
self,
selector: #selector(screenUnlocked),
name: NSNotification.Name("com.apple.screenIsUnlocked"),
object: nil
)
Globals.dnc.addObserver(
forName: NSNotification.Name("com.apple.screenIsLocked"),
object: nil,
queue: .main) { _ in
nudgePrimaryState.screenCurrentlyLocked = true
utilsLog.info("Screen was locked")
}
Globals.dnc.addObserver(
forName: NSNotification.Name("com.apple.screenIsUnlocked"),
object: nil,
queue: .main) { _ in
nudgePrimaryState.screenCurrentlyLocked = false
utilsLog.info("Screen was unlocked")
}
}

private func setupWorkspaceNotificationCenterObservers() {
Expand Down Expand Up @@ -541,11 +537,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return
}

let shouldRunAsynchronously = OptionalFeatureVariables.asynchronousSoftwareUpdate &&
!AppStateManager().requireMajorUpgrade() &&
!DateManager().pastRequiredInstallationDate()

if shouldRunAsynchronously {
if OptionalFeatureVariables.asynchronousSoftwareUpdate {
runUpdateAsynchronously()
} else {
SoftwareUpdate().download()
Expand Down
6 changes: 3 additions & 3 deletions Nudge/Utilities/UILogic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private func logUserSessionDeferrals(resetCount: Bool = false) {
}

func needToActivateNudge() -> Bool {
if NSApplication.shared.isActive {
if NSApplication.shared.isActive && nudgeLogState.afterFirstLaunch {
LogManager.notice("Nudge is currently the frontmostApplication", logger: uiLog)
return false
}
Expand Down Expand Up @@ -354,7 +354,7 @@ private func updateDualQuitButtonRequirement() {
nudgePrimaryState.requireDualQuitButtons = AppStateManager().requireDualQuitButtons() || nudgePrimaryState.userDeferrals > deferralThreshold
}

private func updateNudgeState() {
func updateNudgeState() {
nudgePrimaryState.deferralCountPastThreshold = nudgePrimaryState.userDeferrals > UserExperienceVariables.allowedDeferrals

if nudgePrimaryState.userDeferrals > UserExperienceVariables.allowedDeferralsUntilForcedSecondaryQuitButton {
Expand All @@ -363,7 +363,7 @@ private func updateNudgeState() {

if nudgePrimaryState.deferralCountPastThreshold {
if !nudgePrimaryState.hasLoggedDeferralCountPastThreshold {
LogManager.notice("allowedDeferrals has been passed", logger: uiLog)
LogManager.notice("allowedDeferrals has been passed: \(UserExperienceVariables.allowedDeferrals)", logger: uiLog)
nudgePrimaryState.hasLoggedDeferralCountPastThreshold = true
}
}
Expand Down
70 changes: 43 additions & 27 deletions Nudge/Utilities/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,24 @@ struct AppStateManager {
}

private func applyBackgroundBlur(to window: NSWindow) {
// load the blur background and send it to the back if we are past the required install date
LogManager.info("Enabling blurred background", logger: uiLog)
nudgePrimaryState.backgroundBlur.removeAll()
// Figure out all the screens upon Nudge launching
UIConstants.screens.forEach { screen in
let blurWindowController = BackgroundBlurWindowController()
nudgePrimaryState.backgroundBlur.append(blurWindowController)
blurWindowController.close()
blurWindowController.loadWindow()
blurWindowController.showWindow(nil)
loopedScreen = screen
}
// load the blur background and send it to the back if we are past the required install date
if nudgePrimaryState.backgroundBlur.isEmpty {
LogManager.info("Enabling blurred background", logger: uiLog)
UIConstants.screens.forEach { screen in
let blurWindowController = BackgroundBlurWindowController()
blurWindowController.loadWindow()
blurWindowController.showWindow(nil)
loopedScreen = screen
nudgePrimaryState.backgroundBlur.append(blurWindowController)
}
window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow) + 1))
} else {
LogManager.info("Background blur currently set", logger: uiLog)
}
window.level = .floating
}

private func calculateNewRequiredInstallationDateIfNeeded(currentDate: Date, gracePeriodPathCreationDate: Date) -> Date {
Expand Down Expand Up @@ -774,8 +781,8 @@ struct UIUtilities {
private func determineUpdateURL() -> URL? {
if let actionButtonPath = FeatureVariables.actionButtonPath {
if actionButtonPath.isEmpty {
LogManager.warning("actionButtonPath is set but contains an empty string. No action will be triggered.", logger: utilsLog)
return nil
LogManager.warning("actionButtonPath is set but contains an empty string. Defaulting to out of box behavior.", logger: utilsLog)
return URL(fileURLWithPath: "/System/Library/CoreServices/Software Update.app")
}

// Check if it's a shell command
Expand Down Expand Up @@ -804,7 +811,7 @@ struct UIUtilities {
return URL(fileURLWithPath: "/System/Library/CoreServices/Software Update.app")
}

func executeShellCommand(command: String, userClicked: Bool, configuration: NSWorkspace.OpenConfiguration) {
func executeShellCommand(command: String, userClicked: Bool) {
let cmds = command.components(separatedBy: " ")
guard let launchPath = cmds.first, let argument = cmds.last else {
LogManager.error("Invalid shell command format", logger: uiLog)
Expand Down Expand Up @@ -849,10 +856,14 @@ struct UIUtilities {
if userClicked {
LogManager.notice("User clicked updateDevice", logger: uiLog)
// Remove forced blur and reset window level
nudgePrimaryState.backgroundBlur.forEach { blurWindowController in
blurWindowController.close()
if !nudgePrimaryState.backgroundBlur.isEmpty {
nudgePrimaryState.backgroundBlur.forEach { blurWindowController in
uiLog.notice("\("Attempting to remove forced blur", privacy: .public)")
blurWindowController.close()
nudgePrimaryState.backgroundBlur.removeAll()
}
NSApp.windows.first?.level = .normal
}
NSApp.windows.first?.level = .normal
} else {
LogManager.notice("Synthetically clicked updateDevice due to allowedDeferral count", logger: uiLog)
}
Expand All @@ -872,22 +883,27 @@ struct UIUtilities {
let configuration = NSWorkspace.OpenConfiguration()
configuration.activates = true

guard let url = determineUpdateURL() else {
return
}
if let url = determineUpdateURL() {
let openAction = {
if url.isFileURL {
NSWorkspace.shared.openApplication(at: url, configuration: configuration)
} else {
NSWorkspace.shared.open(url)
}
}

let openAction = {
if url.isFileURL {
NSWorkspace.shared.openApplication(at: url, configuration: configuration)
// Execute the action immediately or with a delay based on user interaction
if userClicked {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: openAction)
} else {
NSWorkspace.shared.open(url)
openAction()
}
}

if userClicked {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: openAction)
} else {
openAction()
if let actionButtonPath = FeatureVariables.actionButtonPath {
executeShellCommand(command: actionButtonPath, userClicked: userClicked)
} else {
LogManager.error("actionButtonPath is nil.", logger: uiLog)
}
}

postUpdateDeviceActions(userClicked: userClicked)
Expand Down
Loading