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
6 changes: 4 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ jobs:
-derivedDataPath build \
-destination 'platform=macOS' \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGN_STYLE=Automatic \
MACOSX_DEPLOYMENT_TARGET=26.0 \
ONLY_ACTIVE_ARCH=NO \
clean build

- name: Create DMG
id: create_dmg
run: |
# Ensure ad-hoc signing for the app bundle
codesign --force --sign - build/Build/Products/Release/${{ env.PROJECT_NAME }}.app

mkdir -p dmg_temp
cp -R build/Build/Products/Release/${{ env.PROJECT_NAME }}.app dmg_temp/
ln -sf /Applications dmg_temp/Applications
Expand Down
27 changes: 23 additions & 4 deletions ScreenTranslate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
SC000008 /* Sources */,
SC000003 /* Frameworks */,
SC000009 /* Resources */,
SC000020 /* Sign Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -137,6 +138,22 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
SC000020 /* Sign Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Sign embedded frameworks with ad-hoc signature to match main app\n# This is needed when using ad-hoc signing for the main app\nif [ \"$CODE_SIGN_IDENTITY\" = \"-\" ] || [ -z \"$CODE_SIGN_IDENTITY\" ]; then\n FRAMEWORKS_DIR=\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Frameworks\"\n if [ -d \"$FRAMEWORKS_DIR\" ]; then\n for FRAMEWORK in \"$FRAMEWORKS_DIR\"/*.framework; do\n if [ -d \"$FRAMEWORK\" ]; then\n echo \"Signing framework with ad-hoc: $(basename \"$FRAMEWORK\")\"\n codesign --force --sign - --deep \"$FRAMEWORK\"\n fi\n done\n fi\nfi\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
SC000008 /* Sources */ = {
isa = PBXSourcesBuildPhase;
Expand Down Expand Up @@ -277,10 +294,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "ScreenTranslate/Supporting Files/ScreenTranslate.entitlements";
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = 7GT4893YFC;
DEVELOPMENT_TEAM = "";
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand All @@ -298,6 +315,7 @@
MARKETING_VERSION = 1.3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.screentranslate.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
Expand All @@ -311,10 +329,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "ScreenTranslate/Supporting Files/ScreenTranslate.entitlements";
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = 7GT4893YFC;
DEVELOPMENT_TEAM = "";
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand All @@ -332,6 +350,7 @@
MARKETING_VERSION = 1.3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.screentranslate.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
Expand Down
31 changes: 26 additions & 5 deletions ScreenTranslate/Features/Onboarding/OnboardingViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,30 @@ final class OnboardingViewModel {
func checkPermissions() {
hasAccessibilityPermission = AccessibilityPermissionChecker.hasPermission

// Check screen recording using CGPreflightScreenCaptureAccess
// This API is deprecated in macOS 15 but still works and does NOT trigger dialog
hasScreenRecordingPermission = CGPreflightScreenCaptureAccess()
// Check screen recording permission using multiple methods for reliability
hasScreenRecordingPermission = checkScreenRecordingPermission()
}

/// Checks screen recording permission using multiple methods for reliability
private func checkScreenRecordingPermission() -> Bool {
// Method 1: CGPreflightScreenCaptureAccess (may not work in all cases)
if CGPreflightScreenCaptureAccess() {
return true
}

// Method 2: Check if we can see windows from other apps
// If we have permission, we should see windows from other apps
let windowList = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID) as? [[String: Any]] ?? []
let ownPID = ProcessInfo.processInfo.processIdentifier

// Count windows from other processes
let otherAppWindows = windowList.filter { window in
guard let ownerPID = window[kCGWindowOwnerPID as String] as? Int32 else { return false }
return ownerPID != ownPID
}

// If we can see windows from other apps, we likely have permission
return otherAppWindows.count > 3
}

/// Requests screen recording permission
Expand Down Expand Up @@ -213,8 +234,8 @@ final class OnboardingViewModel {

switch type {
case .screenRecording:
// Use CGPreflightScreenCaptureAccess to check without triggering dialog
let granted = CGPreflightScreenCaptureAccess()
// Use multiple methods to check permission without triggering dialog
let granted = checkScreenRecordingPermission()
if granted {
hasScreenRecordingPermission = true
permissionCheckTask = nil
Expand Down
29 changes: 26 additions & 3 deletions ScreenTranslate/Features/Settings/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,13 +375,36 @@ final class SettingsViewModel {
// Check folder access permission by testing if we can write to the save location
hasFolderAccessPermission = checkFolderAccess(to: saveLocation)

// Check screen recording permission using CGPreflightScreenCaptureAccess
// This API is deprecated in macOS 15 but still works and does NOT trigger dialog
hasScreenRecordingPermission = CGPreflightScreenCaptureAccess()
// Check screen recording permission
// Try CGPreflightScreenCaptureAccess first, then fallback to window count check
hasScreenRecordingPermission = checkScreenRecordingPermission()

isCheckingPermissions = false
}

/// Checks screen recording permission using multiple methods for reliability
private func checkScreenRecordingPermission() -> Bool {
// Method 1: CGPreflightScreenCaptureAccess (may not work in all cases)
if CGPreflightScreenCaptureAccess() {
return true
}

// Method 2: Check if we can see windows from other apps
// If we have permission, we should see windows from other apps
let windowList = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID) as? [[String: Any]] ?? []
let ownPID = ProcessInfo.processInfo.processIdentifier

// Count windows from other processes
let otherAppWindows = windowList.filter { window in
guard let ownerPID = window[kCGWindowOwnerPID as String] as? Int32 else { return false }
return ownerPID != ownPID
}

// If we can see windows from other apps, we likely have permission
// (There should be at least a few windows from Finder, Dock, etc.)
return otherAppWindows.count > 3
}

/// Checks if we have write access to the specified folder
private func checkFolderAccess(to url: URL) -> Bool {
let fileManager = FileManager.default
Expand Down