diff --git a/README.md b/README.md index fb66160..5df0016 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ -Recording Indicator Utility App icon +### You may not need Recording Indicator Utility -
-

Recording Indicator Utility

-

In macOS Sonoma 14.4 or later, you can hide the recording indicator on external displays without using Recording Indicator Utility. Learn more ↗

-

Recording Indicator Utility lets you turn off the orange microphone recording indicator light, making it ideal for professionals who run live events, record screencasts, or use a system-wide volume equalizer. Recording Indicator Utility supports macOS Sonoma, macOS Ventura and macOS Monterey.

+You can follow these steps to [hide the recording indicator on external displays](https://support.apple.com/118449) without using Recording Indicator Utility. + +1. [Update to the latest version of macOS](https://support.apple.com/en-us/108382). +1. Start up from macOS Recovery. [Instructions for Mac with Apple silicon](https://support.apple.com/guide/mac-help/mchl82829c17/mac#mchl5abfbb29). [Instructions for Intel-based Mac](https://support.apple.com/guide/mac-help/mchl338cf9a8#mchl69906860). +1. From the Utilities menu in the menu bar, choose Terminal. +1. Enter this command: `system-override suppress-sw-camera-indication-on-external-displays=on` +1. Quit Terminal. +1. From the Apple menu, choose Restart. +1. After your Mac restarts, choose Apple menu  > System Settings, then click Privacy & Security in the sidebar. +1. Click Microphone (or Camera). +1. Turn off Privacy Indicators for Microphone (or Camera) Access. + + +--- + +### Downloading Recording Indicator Utility + +

You can use Recording Indicator Utility to turn off the recording indicator on the primary display and external displays not in full screen. This is ideal for professionals who run live events, record screencasts, or use a system-wide volume equalizer.

-

Download Recording Indicator Utility @@ -22,12 +35,10 @@ After downloading Recording Indicator Utility, double click to open it. macOS may prompt you “Recording Indicator Utility cannot be opened because it is from an unidentified developer.” This is expected. -To open Recording Indicator Utility, [right-click](https://support.apple.com/HT207700) on Recording Indicator Utility in Finder, and click “Open” as shown below. +To open Recording Indicator Utility, navigate to System Settings > Privacy & Security, then scroll down and click [“Open Anyway”](https://support.apple.com/102445#openanyway). ![](RecordingIndicatorUtility/Screenshots/screenshot-gatekeeper.jpg) -If you right-clicked when opening Recording Indicator Utility for the first time, you may need to [right-click](https://support.apple.com/HT207700) for a second time. If Recording Indicator Utility still can’t be opened, [check your GateKeeper settings](https://support.apple.com/en-us/HT202491) under the General tab in System Preferences > Security & Privacy. Click “Open Anyway” to open Recording Indicator Utility. - Recording Indicator Utility will not harm your Mac. This alert shows up because Recording Indicator Utility modifies macOS and cannot be notarized. Recording Indicator Utility is [open source](https://github.com/cormiertyshawn895/RecordingIndicatorUtility), so you can always [examine its source code](https://github.com/cormiertyshawn895/RecordingIndicatorUtility/tree/master/RecordingIndicatorUtility) to verify its inner working. --- @@ -69,7 +80,7 @@ Open Recording Indicator Utility, turn on the recording indicator, then click No. Your admin password is required before Recording Indicator Utility can make any changes. Recording Indicator Utility is designed to support professionals who [run live events](https://cdm.link/2021/12/apple-added-an-orange-dot-thats-a-showstopper-for-live-visuals-and-it-needs-a-fix/), [record screencasts](https://telestreamforum.forumbee.com/t/x2hgw5f/orange-dot-on-imac-screen-when-screenflow-is-recording), or use a [system-wide volume equalizer](https://www.globaldelight.com/faq/boom3dmac). #### How does Recording Indicator Utility differ from undot and YellowDot? -Recording Indicator Utility supports the latest version of [macOS Sonoma](https://apps.apple.com/app/macos-sonoma/id6450717509), [macOS Ventura](https://apps.apple.com/app/macos-ventura/id1638787999) and [macOS Monterey](https://apps.apple.com/app/macos-monterey/id1576738294). It requires adjusting security settings. +Recording Indicator Utility supports the latest version of macOS, but requires adjusting security settings. [undot](https://github.com/s4y/undot) and [YellowDot](https://lowtechguys.com/yellowdot/) only support macOS 12.1 and earlier, allow you to turn off the recording indicator in full-screen apps, and do not require adjusting security settings. diff --git a/Recording Indicator Utility.xcodeproj/project.pbxproj b/Recording Indicator Utility.xcodeproj/project.pbxproj index 5055a78..cce554f 100644 --- a/Recording Indicator Utility.xcodeproj/project.pbxproj +++ b/Recording Indicator Utility.xcodeproj/project.pbxproj @@ -429,7 +429,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = RecordingIndicatorUtility/RecordingIndicatorUtility.entitlements; CODE_SIGN_IDENTITY = "-"; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 16; DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "Recording Indicator Utility"; @@ -457,7 +457,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = RecordingIndicatorUtility/RecordingIndicatorUtility.entitlements; CODE_SIGN_IDENTITY = "-"; - CURRENT_PROJECT_VERSION = 14; + CURRENT_PROJECT_VERSION = 16; DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "Recording Indicator Utility"; diff --git a/Recording Indicator Utility.xcodeproj/xcshareddata/xcschemes/Recording Indicator Utility.xcscheme b/Recording Indicator Utility.xcodeproj/xcshareddata/xcschemes/Recording Indicator Utility.xcscheme index 6f925f7..91a34ae 100644 --- a/Recording Indicator Utility.xcodeproj/xcshareddata/xcschemes/Recording Indicator Utility.xcscheme +++ b/Recording Indicator Utility.xcodeproj/xcshareddata/xcschemes/Recording Indicator Utility.xcscheme @@ -79,7 +79,7 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = "cp -R "$SRCROOT/RecordingIndicatorUtility/Supplemental/Help.webloc" "$ARCHIVE_PATH/Products/Applications" MARKETING_FOLDER="$PRODUCT_NAME $MARKETING_VERSION" MARKETING_PATH="$ARCHIVE_PATH/Products/$MARKETING_FOLDER" mv "$ARCHIVE_PATH/Products/Applications" "$MARKETING_PATH" ">

+ + + + + + + @@ -430,7 +437,11 @@ CA - + + + + + diff --git a/RecordingIndicatorUtility/Core/AppDelegate.swift b/RecordingIndicatorUtility/Core/AppDelegate.swift index ec27374..e898d29 100644 --- a/RecordingIndicatorUtility/Core/AppDelegate.swift +++ b/RecordingIndicatorUtility/Core/AppDelegate.swift @@ -10,9 +10,16 @@ import Cocoa @main class AppDelegate: NSObject, NSApplicationDelegate { var shouldPreventClosing = false + @IBOutlet weak var showSystemOverrideMenuItem: NSMenuItem! func applicationDidFinishLaunching(_ aNotification: Notification) { NSApp.activate(ignoringOtherApps: true) + if (!osAtLeastSonomaE) { + showSystemOverrideMenuItem.isHidden = true + } + if (osAtLeastSonomaE && !UserDefaults.standard.bool(forKey: "AcknowledgedSystemOverrideAlert") && SystemInformation.shared.isSystemstatusdLoaded) { + self.showSystemOverrideInstructions(self) + } } func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { @@ -35,7 +42,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { text: SystemInformation.shared.newVersionChangelog ?? "A newer version of Recording Indicator Utility is available.", firstButtonText: "Download", secondButtonText: "Learn More...", - thirdButtonText: "Cancel") { (response) in + thirdButtonText: "Cancel") { (response, isChecked) in if (response == .alertFirstButtonReturn) { AppDelegate.current.safelyOpenURL(SystemInformation.shared.latestZIP) } else if (response == .alertSecondButtonReturn) { @@ -47,7 +54,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { text:"", firstButtonText: "OK", secondButtonText: "View Release Page...", - thirdButtonText: "") { (response) in + thirdButtonText: "") { (response, isChecked) in if (response == .alertSecondButtonReturn) { AppDelegate.current.safelyOpenURL(SystemInformation.shared.releasePage) } @@ -55,6 +62,23 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + @IBAction func showSystemOverrideInstructions(_ sender: Any) { + UserDefaults.standard.setValue(nil, forKey: "AcknowledgedSystemOverrideAlert") + AppDelegate.showOptionSheet(title: "You can hide the recording indicator on external displays without using Recording Indicator Utility.", + text: "Start up from macOS Recovery, and enter this command in Terminal:\n\nsystem-override suppress-sw-camera-indication-on-external-displays=on\n\nRestart, then open System Settings > Privacy & Security > Microphone, and turn off Privacy Indicators.", + firstButtonText: "Learn More…", + secondButtonText: " Continue ", + thirdButtonText: "", + checkboxText: "Don’t show this message again") { (response, isChecked) in + if (response == .alertFirstButtonReturn) { + AppDelegate.current.safelyOpenURL("https://support.apple.com/118449") + } + if (isChecked == true) { + UserDefaults.standard.setValue(true, forKey: "AcknowledgedSystemOverrideAlert") + } + } + } + @IBAction func checkForUpdates(_ sender: Any? = nil) { SystemInformation.shared.checkForConfigurationUpdates() if (SystemInformation.shared.hasNewerVersion == true) { @@ -81,7 +105,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @IBAction func issueTracker(_ sender: Any) { - self.safelyOpenURL("https://github.com/cormiertyshawn895/RecordingIndicatorUtility/issues") + self.safelyOpenURL("https://github.com/cormiertyshawn895/RecordingIndicatorUtility/issues?q=") } static var current: AppDelegate { @@ -94,12 +118,17 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - static func showOptionSheet(title: String, text: String, firstButtonText: String, secondButtonText: String, thirdButtonText: String, prefersKeyWindow: Bool = false, callback: @escaping ((_ response: NSApplication.ModalResponse)-> ())) { + static func showOptionSheet(title: String, text: String, firstButtonText: String, secondButtonText: String, thirdButtonText: String, checkboxText: String? = nil, prefersKeyWindow: Bool = false, callback: @escaping ((_ response: NSApplication.ModalResponse, _ isChecked: Bool?)-> ())) { let alert = NSAlert() alert.messageText = title alert.informativeText = text alert.alertStyle = NSAlert.Style.informational alert.addButton(withTitle: firstButtonText) + var checkbox: NSButton? + if let checkboxText = checkboxText { + checkbox = NSButton(checkboxWithTitle: checkboxText, target: nil, action: nil) + alert.accessoryView = checkbox + } if secondButtonText.count > 0 { alert.addButton(withTitle: secondButtonText) } @@ -108,11 +137,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { } if let window = prefersKeyWindow ? NSApp.keyWindow : self.appWindow { alert.beginSheetModal(for: window) { (response) in - callback(response) + callback(response, checkbox?.state == .on) } } else { let response = alert.runModal() - callback(response) + callback(response, checkbox?.state == .on) } } diff --git a/RecordingIndicatorUtility/Extensions/Process-Extension.swift b/RecordingIndicatorUtility/Extensions/Process-Extension.swift index af03386..4192b58 100644 --- a/RecordingIndicatorUtility/Extensions/Process-Extension.swift +++ b/RecordingIndicatorUtility/Extensions/Process-Extension.swift @@ -5,6 +5,9 @@ import Foundation +private let processInfo = ProcessInfo() +let osAtLeastSonomaE = processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 14, minorVersion: 4, patchVersion: 0)) + extension Process { static func runNonAdminTask(toolPath: String, arguments: [String], attemptInteractive: String? = nil) -> String { let task = Process() diff --git a/RecordingIndicatorUtility/Screenshots/screenshot-gatekeeper.jpg b/RecordingIndicatorUtility/Screenshots/screenshot-gatekeeper.jpg index 4fe36a9..5fc9e52 100644 Binary files a/RecordingIndicatorUtility/Screenshots/screenshot-gatekeeper.jpg and b/RecordingIndicatorUtility/Screenshots/screenshot-gatekeeper.jpg differ diff --git a/RecordingIndicatorUtility/Supplemental/Help.webloc b/RecordingIndicatorUtility/Supplemental/Help.webloc new file mode 100644 index 0000000..c13a2e4 --- /dev/null +++ b/RecordingIndicatorUtility/Supplemental/Help.webloc @@ -0,0 +1,8 @@ + + + + + URL + https://support.apple.com/102445#openanyway + + diff --git "a/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pages" "b/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pages" deleted file mode 100644 index 6a004e3..0000000 Binary files "a/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pages" and /dev/null differ diff --git "a/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pdf" "b/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pdf" deleted file mode 100644 index 3b7fdb8..0000000 Binary files "a/RecordingIndicatorUtility/Supplemental/What to do if Recording Indicator Utility can\342\200\231t be opened.pdf" and /dev/null differ diff --git a/RecordingIndicatorUtility/Support/SupportPath.plist b/RecordingIndicatorUtility/Support/SupportPath.plist index 0375bfe..fce7a02 100644 --- a/RecordingIndicatorUtility/Support/SupportPath.plist +++ b/RecordingIndicatorUtility/Support/SupportPath.plist @@ -3,14 +3,14 @@ NewVersionVisibleTitle - Recording Indicator Utility 2.0 (Build 14) contains compatibility improvements. + Recording Indicator Utility 2.0 (Build 16) contains compatibility improvements. NewVersionChangelog • Improves compatibility with macOS LatestZIP https://github.com/cormiertyshawn895/RecordingIndicatorUtility/releases/download/2.0/RecordingIndicatorUtility.2.0.zip LatestBuildNumber - 14 + 16 SupportPathURL https://raw.githubusercontent.com/cormiertyshawn895/RecordingIndicatorUtility/master/RecordingIndicatorUtility/Support/SupportPath.plist ReleasePage diff --git a/RecordingIndicatorUtility/View Controllers/ViewController.swift b/RecordingIndicatorUtility/View Controllers/ViewController.swift index 4ff6bcf..ec9f39a 100644 --- a/RecordingIndicatorUtility/View Controllers/ViewController.swift +++ b/RecordingIndicatorUtility/View Controllers/ViewController.swift @@ -113,7 +113,7 @@ class ViewController: NSViewController { text: "This version of Recording Indicator Utility is only designed and tested for macOS Ventura and macOS Monterey, and does not support macOS \(osFullVersion.majorVersion).", firstButtonText: "Check for Updates", secondButtonText: "Continue Anyway", - thirdButtonText: "Quit") { (response) in + thirdButtonText: "Quit") { (response, isChecked) in if (response == .alertFirstButtonReturn) { AppDelegate.current.checkForUpdates() } else if (response == .alertSecondButtonReturn) { @@ -152,7 +152,7 @@ class ViewController: NSViewController { self.waitingForRestart = SystemInformation.shared.lastSystemBootTime self.updateUI() let title = loaded ? "Turning on the recording indicator light requires a restart to take effect." : "Turning off the recording indicator light requires a restart to take effect." - AppDelegate.showOptionSheet(title: title, text: "Do you want to restart now?", firstButtonText: "Restart Now", secondButtonText: "Not Now", thirdButtonText: "") { response in + AppDelegate.showOptionSheet(title: title, text: "Do you want to restart now?", firstButtonText: "Restart Now", secondButtonText: "Not Now", thirdButtonText: "") { response, isChecked in if (response == .alertFirstButtonReturn) { self.performReboot() } @@ -231,7 +231,7 @@ class ViewController: NSViewController { return } - AppDelegate.showOptionSheet(title: "Would you like to raise security settings?", text: "This will re-enable System Integrity Protection. Your Mac will automatically restart afterwards.", firstButtonText: "Continue and Restart", secondButtonText: "Cancel", thirdButtonText: "") { response in + AppDelegate.showOptionSheet(title: "Would you like to raise security settings?", text: "This will re-enable System Integrity Protection. Your Mac will automatically restart afterwards.", firstButtonText: "Continue and Restart", secondButtonText: "Cancel", thirdButtonText: "") { response, isChecked in if (response == .alertFirstButtonReturn) { self.applyRaisedSecuritySettings() }