Skip to content

Commit

Permalink
Merge pull request #45 from purejava/newer-start-at-login
Browse files Browse the repository at this point in the history
Use ServiceManagement API to enable/disable login items (used to start an app after login)
  • Loading branch information
infeo authored May 29, 2024
2 parents b92c684 + 57bac69 commit 656f2a4
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 60 deletions.
8 changes: 8 additions & 0 deletions src/main/native/Integrations.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
74BEF1852C0384FB006731AC /* SKYLaunchService.m in Sources */ = {isa = PBXBuildFile; fileRef = 74BEF1842C0384FB006731AC /* SKYLaunchService.m */; };
74BEF1862C0384FB006731AC /* SKYLaunchService.h in Headers */ = {isa = PBXBuildFile; fileRef = 74BEF1832C0384FB006731AC /* SKYLaunchService.h */; };
74CF2E7B254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m in Sources */ = {isa = PBXBuildFile; fileRef = 74CF2E7A254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m */; };
9E0819C91D75CABC000C89E6 /* org_cryptomator_macos_keychain_MacKeychain_Native.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E0819C71D75CABC000C89E6 /* org_cryptomator_macos_keychain_MacKeychain_Native.m */; };
9E21340025542735006EA872 /* org_cryptomator_macos_uiappearance_AppAppearance_Native.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E2133FF25542735006EA872 /* org_cryptomator_macos_uiappearance_AppAppearance_Native.m */; };
Expand All @@ -19,6 +21,8 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
74BEF1832C0384FB006731AC /* SKYLaunchService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKYLaunchService.h; sourceTree = "<group>"; };
74BEF1842C0384FB006731AC /* SKYLaunchService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SKYLaunchService.m; sourceTree = "<group>"; };
74CF2E7A254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = org_cryptomator_macos_autostart_MacLaunchServices_Native.m; sourceTree = "<group>"; };
9E0819B71D748ECC000C89E6 /* libIntegrations.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libIntegrations.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
9E0819C71D75CABC000C89E6 /* org_cryptomator_macos_keychain_MacKeychain_Native.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = org_cryptomator_macos_keychain_MacKeychain_Native.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -54,6 +58,8 @@
9E21341025542ECE006EA872 /* SKYAppearanceNotifier.m */,
9E213409255429CA006EA872 /* SKYAppearanceObserver.h */,
9E21340A255429CA006EA872 /* SKYAppearanceObserver.m */,
74BEF1832C0384FB006731AC /* SKYLaunchService.h */,
74BEF1842C0384FB006731AC /* SKYLaunchService.m */,
9E0819B81D748ECC000C89E6 /* Products */,
);
sourceTree = "<group>";
Expand All @@ -74,6 +80,7 @@
buildActionMask = 2147483647;
files = (
9E21341125542ECE006EA872 /* SKYAppearanceNotifier.h in Headers */,
74BEF1862C0384FB006731AC /* SKYLaunchService.h in Headers */,
9E21340B255429CA006EA872 /* SKYAppearanceObserver.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -135,6 +142,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74BEF1852C0384FB006731AC /* SKYLaunchService.m in Sources */,
9EACB1B425557865000F3214 /* org_cryptomator_macos_tray_ActivationPolicy_Native.m in Sources */,
74CF2E7B254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m in Sources */,
9E21340C255429CA006EA872 /* SKYAppearanceObserver.m in Sources */,
Expand Down
17 changes: 17 additions & 0 deletions src/main/native/SKYLaunchService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// SKYLaunchService.h
// Integrations
//
// Created by Tobias Hagemann on 26.05.24.
// Copyright © 2024 Cryptomator. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SKYLaunchService : NSObject

+ (BOOL)isLoginItemEnabled;
+ (BOOL)enableLoginItem;
+ (BOOL)disableLoginItem;

@end
128 changes: 128 additions & 0 deletions src/main/native/SKYLaunchService.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//
// SKYLaunchService.m
// Integrations
//
// Created by Tobias Hagemann on 26.05.24.
// Copyright © 2024 Cryptomator. All rights reserved.
//

#import "SKYLaunchService.h"
#import <ServiceManagement/ServiceManagement.h>

@implementation SKYLaunchService

+ (BOOL)isLoginItemEnabled {
if (@available(macOS 13, *)) {
if (SMAppService.mainAppService.status == SMAppServiceStatusEnabled) {
return YES;
} else if ([self isLegacyLoginItemEnabled]) {
// migrate legacy login item
[self disableLegacyLoginItem];
return [self enableLoginItem];
} else {
return NO;
}
} else { // macOS < 13
return [self isLegacyLoginItemEnabled];
}
}

+ (BOOL)enableLoginItem {
if (@available(macOS 13, *)) {
NSError *error;
if ([SMAppService.mainAppService registerAndReturnError:&error]) {
return YES;
} else {
NSLog(@"Failed to register login item: %@", error.localizedDescription);
return NO;
}
} else { // macOS < 13
return [self enableLegacyLoginItem];
}
}

+ (BOOL)disableLoginItem {
if (@available(macOS 13, *)) {
NSError *error;
if ([SMAppService.mainAppService unregisterAndReturnError:&error]) {
return YES;
} else {
NSLog(@"Failed to unregister login item: %@", error.localizedDescription);
return NO;
}
} else { // macOS < 13
return [self disableLegacyLoginItem];
}
}

#pragma mark - Legacy

+ (BOOL)isLegacyLoginItemEnabled {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
if (sharedFileList) {
UInt32 seedValue;
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
for (id sharedFile in sharedFileListArray) {
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
CFURLRef applicationPathURL = NULL;
LSSharedFileListItemResolve(sharedFileListItem, 0, (CFURLRef *)&applicationPathURL, NULL);
if (applicationPathURL != NULL) {
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
CFRelease(applicationPathURL);
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
CFRelease(sharedFileList);
return YES;
}
}
}
CFRelease(sharedFileList);
} else {
NSLog(@"Unable to create the shared file list.");
}
return NO;
}

+ (BOOL)enableLegacyLoginItem {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
NSURL *applicationPathURL = [NSURL fileURLWithPath:applicationPath];
if (sharedFileList) {
LSSharedFileListItemRef sharedFileListItem = LSSharedFileListInsertItemURL(sharedFileList, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)applicationPathURL, NULL, NULL);
if (sharedFileListItem) {
CFRelease(sharedFileListItem);
}
CFRelease(sharedFileList);
return YES;
} else {
NSLog(@"Unable to create the shared file list.");
return NO;
}
}

+ (BOOL)disableLegacyLoginItem {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
if (sharedFileList) {
UInt32 seedValue;
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
for (id sharedFile in sharedFileListArray) {
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
CFURLRef applicationPathURL;
if (LSSharedFileListItemResolve(sharedFileListItem, 0, &applicationPathURL, NULL) == noErr) {
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
LSSharedFileListItemRemove(sharedFileList, sharedFileListItem);
}
CFRelease(applicationPathURL);
}
}
CFRelease(sharedFileList);
return YES;
} else {
NSLog(@"Unable to create the shared file list.");
return NO;
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,16 @@
//

#import "org_cryptomator_macos_autostart_MacLaunchServices_Native.h"
#import <Foundation/Foundation.h>
#import "SKYLaunchService.h"

JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_isLoginItemEnabled(JNIEnv *env, jobject thisObj) {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
if (sharedFileList) {
UInt32 seedValue;
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
for (id sharedFile in sharedFileListArray) {
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
CFURLRef applicationPathURL = NULL;
LSSharedFileListItemResolve(sharedFileListItem, 0, (CFURLRef *)&applicationPathURL, NULL);
if (applicationPathURL != NULL) {
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
CFRelease(applicationPathURL);
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
CFRelease(sharedFileList);
return YES;
}
}
}
CFRelease(sharedFileList);
} else {
NSLog(@"Unable to create the shared file list.");
}
return NO;
return [SKYLaunchService isLoginItemEnabled];
}

JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_enableLoginItem(JNIEnv *env, jobject thisObj) {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
NSURL *applicationPathURL = [NSURL fileURLWithPath:applicationPath];
if (sharedFileList) {
LSSharedFileListItemRef sharedFileListItem = LSSharedFileListInsertItemURL(sharedFileList, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)applicationPathURL, NULL, NULL);
if (sharedFileListItem) {
CFRelease(sharedFileListItem);
}
CFRelease(sharedFileList);
return YES;
} else {
NSLog(@"Unable to create the shared file list.");
return NO;
}
return [SKYLaunchService enableLoginItem];
}

JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_disableLoginItem(JNIEnv *env, jobject thisObj) {
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
if (sharedFileList) {
UInt32 seedValue;
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
for (id sharedFile in sharedFileListArray) {
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
CFURLRef applicationPathURL;
if (LSSharedFileListItemResolve(sharedFileListItem, 0, &applicationPathURL, NULL) == noErr) {
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
LSSharedFileListItemRemove(sharedFileList, sharedFileListItem);
}
CFRelease(applicationPathURL);
}
}
CFRelease(sharedFileList);
return YES;
} else {
NSLog(@"Unable to create the shared file list.");
return NO;
}
return [SKYLaunchService disableLoginItem];
}

0 comments on commit 656f2a4

Please sign in to comment.