Skip to content
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
7 changes: 6 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ on:

jobs:
build:
runs-on: macos-14
runs-on: macos-15
steps:
- uses: actions/checkout@v4

- name: Select Xcode 16 (Swift 6.1 for PermissionFlow)
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16'

- name: Build Aware
run: |
xcodebuild -scheme Aware -configuration Debug -derivedDataPath build \
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/publish-appcast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ permissions:

jobs:
build-appcast:
runs-on: macos-14
runs-on: macos-15
steps:
- uses: actions/checkout@v4

- name: Select Xcode 16 (Swift 6.1 for PermissionFlow)
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16'

- name: Resolve Sparkle tools
run: |
xcodebuild -resolvePackageDependencies -project Aware.xcodeproj -scheme Aware -derivedDataPath build -quiet
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ env:

jobs:
build:
runs-on: macos-14
runs-on: macos-15
outputs:
sparkle_enabled: ${{ steps.sparkle_settings.outputs.enabled }}
steps:
- uses: actions/checkout@v4

- name: Select Xcode 16 (Swift 6 for DMGMaker)
- name: Select Xcode 16 (Swift 6 for DMGMaker, 6.1 for PermissionFlow)
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16'
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Repo-agnostic skills (`sharpen-the-tip`, `prototype-mode`, `housekeeping`, `proa
## Local Divergence

- Build verification: `xcodebuild -scheme Aware -configuration Debug -derivedDataPath build build`
- PermissionFlow comes from the LPFchan/PermissionFlow fork, pinned to `2.11.2-swift6.1`: upstream v2.11.2 with `swift-tools-version` lowered to 6.1 so Xcode 16.3+ can build it. Move to a new fork tag when upstream updates.
- Commit provenance setup: `scripts/install-hooks.sh` configures the tracked `commit-msg` hook locally.
- Commit provenance checks: `scripts/check-commit-standards.sh <commit-message-file>` and `scripts/check-commit-range.sh <base> <head>`
- There is no dedicated automated test suite in the repo today. For runtime changes, use the build plus focused manual validation.
Expand Down
25 changes: 25 additions & 0 deletions Aware.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
A1B2C3D40003 /* PresenceDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D10003 /* PresenceDetector.swift */; };
A1B2C3D40004 /* SleepAssertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D10004 /* SleepAssertion.swift */; };
E1F2A3B40000 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E1F2A3B40002 /* Sparkle */; };
F5A6B7C80004 /* PermissionFlow in Frameworks */ = {isa = PBXBuildFile; productRef = F5A6B7C80002 /* PermissionFlow */; };
F5A6B7C80005 /* PermissionFlowCameraStatus in Frameworks */ = {isa = PBXBuildFile; productRef = F5A6B7C80003 /* PermissionFlowCameraStatus */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -41,6 +43,8 @@
buildActionMask = 2147483647;
files = (
E1F2A3B40000 /* Sparkle in Frameworks */,
F5A6B7C80004 /* PermissionFlow in Frameworks */,
F5A6B7C80005 /* PermissionFlowCameraStatus in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -123,6 +127,8 @@
name = Aware;
packageProductDependencies = (
E1F2A3B40002 /* Sparkle */,
F5A6B7C80002 /* PermissionFlow */,
F5A6B7C80003 /* PermissionFlowCameraStatus */,
);
productName = Aware;
productReference = A1B2C3D00001 /* Aware.app */;
Expand Down Expand Up @@ -157,6 +163,7 @@
mainGroup = A1B2C3D30001;
packageReferences = (
E1F2A3B40001 /* XCRemoteSwiftPackageReference "Sparkle" */,
F5A6B7C80001 /* XCRemoteSwiftPackageReference "PermissionFlow" */,
);
productRefGroup = A1B2C3D30003 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -350,6 +357,14 @@
minimumVersion = 2.7.0;
};
};
F5A6B7C80001 /* XCRemoteSwiftPackageReference "PermissionFlow" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/LPFchan/PermissionFlow";
requirement = {
kind = exactVersion;
version = "2.11.2-swift6.1";
Comment thread
LPFchan marked this conversation as resolved.
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand All @@ -358,6 +373,16 @@
package = E1F2A3B40001 /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
F5A6B7C80002 /* PermissionFlow */ = {
isa = XCSwiftPackageProductDependency;
package = F5A6B7C80001 /* XCRemoteSwiftPackageReference "PermissionFlow" */;
productName = PermissionFlow;
};
F5A6B7C80003 /* PermissionFlowCameraStatus */ = {
isa = XCSwiftPackageProductDependency;
package = F5A6B7C80001 /* XCRemoteSwiftPackageReference "PermissionFlow" */;
productName = PermissionFlowCameraStatus;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = A1B2C3D20006 /* Project object */;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 23 additions & 9 deletions Aware/MenuBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import AppKit
import AVFoundation
import CoreGraphics
import PermissionFlow
import PermissionFlowCameraStatus
import ServiceManagement

private enum MenuItemTag: Int {
Expand Down Expand Up @@ -63,6 +65,9 @@ final class MenuBarController: NSObject {
private let sleepAssertion = SleepAssertion()
private let checkForUpdatesTarget: AnyObject?
private let checkForUpdatesAction: Selector?
/// Opens the System Settings camera pane when access has been denied.
/// Created lazily on the main actor, where menu actions and alerts run.
private var permissionController: PermissionFlowController?

private var pollingTimer: DispatchSourceTimer?
private let timerQueue = DispatchQueue(label: "com.aware.timer", qos: .userInitiated)
Expand Down Expand Up @@ -93,6 +98,9 @@ final class MenuBarController: NSObject {
self.checkForUpdatesTarget = checkForUpdatesTarget
self.checkForUpdatesAction = checkForUpdatesAction
super.init()
MainActor.assumeIsolated {
PermissionFlowCameraStatus.register()
}
#if DEBUG
debugLog("MenuBarController init started")
#endif
Expand Down Expand Up @@ -349,6 +357,14 @@ final class MenuBarController: NSObject {
alert.alertStyle = .warning
alert.addButton(withTitle: String(localized: "alert.ok"))
alert.runModal()
// Camera uses the system toggle list (no drag panel), so this just
// opens System Settings > Privacy & Security > Camera.
MainActor.assumeIsolated {
if permissionController == nil {
permissionController = PermissionFlow.makeController()
}
permissionController?.authorize(pane: .camera)
}
}

@objc private func handleDisplaySleep() {
Expand All @@ -369,20 +385,18 @@ final class MenuBarController: NSObject {
if isEnabled {
isEnabled = false
} else {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
let cameraPermission = CameraPermissionStatusProvider()
if cameraPermission.authorizationState() == .granted {
isEnabled = true
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
} else if AVCaptureDevice.authorizationStatus(for: .video) == .notDetermined {
// System prompt via PermissionFlow's camera status provider.
cameraPermission.requestAuthorization { [weak self] state in
DispatchQueue.main.async {
self?.isEnabled = granted
self?.isEnabled = (state == .granted)
}
}
case .denied, .restricted:
} else {
showCameraDeniedAlert()
@unknown default:
break
}
}
}
Expand Down
Loading