Skip to content

Commit 656f2a4

Browse files
authored
Merge pull request #45 from purejava/newer-start-at-login
Use ServiceManagement API to enable/disable login items (used to start an app after login)
2 parents b92c684 + 57bac69 commit 656f2a4

File tree

4 files changed

+157
-60
lines changed

4 files changed

+157
-60
lines changed

src/main/native/Integrations.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

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

2123
/* Begin PBXFileReference section */
24+
74BEF1832C0384FB006731AC /* SKYLaunchService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKYLaunchService.h; sourceTree = "<group>"; };
25+
74BEF1842C0384FB006731AC /* SKYLaunchService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SKYLaunchService.m; sourceTree = "<group>"; };
2226
74CF2E7A254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = org_cryptomator_macos_autostart_MacLaunchServices_Native.m; sourceTree = "<group>"; };
2327
9E0819B71D748ECC000C89E6 /* libIntegrations.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libIntegrations.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
2428
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>"; };
@@ -54,6 +58,8 @@
5458
9E21341025542ECE006EA872 /* SKYAppearanceNotifier.m */,
5559
9E213409255429CA006EA872 /* SKYAppearanceObserver.h */,
5660
9E21340A255429CA006EA872 /* SKYAppearanceObserver.m */,
61+
74BEF1832C0384FB006731AC /* SKYLaunchService.h */,
62+
74BEF1842C0384FB006731AC /* SKYLaunchService.m */,
5763
9E0819B81D748ECC000C89E6 /* Products */,
5864
);
5965
sourceTree = "<group>";
@@ -74,6 +80,7 @@
7480
buildActionMask = 2147483647;
7581
files = (
7682
9E21341125542ECE006EA872 /* SKYAppearanceNotifier.h in Headers */,
83+
74BEF1862C0384FB006731AC /* SKYLaunchService.h in Headers */,
7784
9E21340B255429CA006EA872 /* SKYAppearanceObserver.h in Headers */,
7885
);
7986
runOnlyForDeploymentPostprocessing = 0;
@@ -135,6 +142,7 @@
135142
isa = PBXSourcesBuildPhase;
136143
buildActionMask = 2147483647;
137144
files = (
145+
74BEF1852C0384FB006731AC /* SKYLaunchService.m in Sources */,
138146
9EACB1B425557865000F3214 /* org_cryptomator_macos_tray_ActivationPolicy_Native.m in Sources */,
139147
74CF2E7B254C295A006266D6 /* org_cryptomator_macos_autostart_MacLaunchServices_Native.m in Sources */,
140148
9E21340C255429CA006EA872 /* SKYAppearanceObserver.m in Sources */,

src/main/native/SKYLaunchService.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// SKYLaunchService.h
3+
// Integrations
4+
//
5+
// Created by Tobias Hagemann on 26.05.24.
6+
// Copyright © 2024 Cryptomator. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface SKYLaunchService : NSObject
12+
13+
+ (BOOL)isLoginItemEnabled;
14+
+ (BOOL)enableLoginItem;
15+
+ (BOOL)disableLoginItem;
16+
17+
@end

src/main/native/SKYLaunchService.m

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//
2+
// SKYLaunchService.m
3+
// Integrations
4+
//
5+
// Created by Tobias Hagemann on 26.05.24.
6+
// Copyright © 2024 Cryptomator. All rights reserved.
7+
//
8+
9+
#import "SKYLaunchService.h"
10+
#import <ServiceManagement/ServiceManagement.h>
11+
12+
@implementation SKYLaunchService
13+
14+
+ (BOOL)isLoginItemEnabled {
15+
if (@available(macOS 13, *)) {
16+
if (SMAppService.mainAppService.status == SMAppServiceStatusEnabled) {
17+
return YES;
18+
} else if ([self isLegacyLoginItemEnabled]) {
19+
// migrate legacy login item
20+
[self disableLegacyLoginItem];
21+
return [self enableLoginItem];
22+
} else {
23+
return NO;
24+
}
25+
} else { // macOS < 13
26+
return [self isLegacyLoginItemEnabled];
27+
}
28+
}
29+
30+
+ (BOOL)enableLoginItem {
31+
if (@available(macOS 13, *)) {
32+
NSError *error;
33+
if ([SMAppService.mainAppService registerAndReturnError:&error]) {
34+
return YES;
35+
} else {
36+
NSLog(@"Failed to register login item: %@", error.localizedDescription);
37+
return NO;
38+
}
39+
} else { // macOS < 13
40+
return [self enableLegacyLoginItem];
41+
}
42+
}
43+
44+
+ (BOOL)disableLoginItem {
45+
if (@available(macOS 13, *)) {
46+
NSError *error;
47+
if ([SMAppService.mainAppService unregisterAndReturnError:&error]) {
48+
return YES;
49+
} else {
50+
NSLog(@"Failed to unregister login item: %@", error.localizedDescription);
51+
return NO;
52+
}
53+
} else { // macOS < 13
54+
return [self disableLegacyLoginItem];
55+
}
56+
}
57+
58+
#pragma mark - Legacy
59+
60+
+ (BOOL)isLegacyLoginItemEnabled {
61+
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
62+
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
63+
if (sharedFileList) {
64+
UInt32 seedValue;
65+
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
66+
for (id sharedFile in sharedFileListArray) {
67+
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
68+
CFURLRef applicationPathURL = NULL;
69+
LSSharedFileListItemResolve(sharedFileListItem, 0, (CFURLRef *)&applicationPathURL, NULL);
70+
if (applicationPathURL != NULL) {
71+
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
72+
CFRelease(applicationPathURL);
73+
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
74+
CFRelease(sharedFileList);
75+
return YES;
76+
}
77+
}
78+
}
79+
CFRelease(sharedFileList);
80+
} else {
81+
NSLog(@"Unable to create the shared file list.");
82+
}
83+
return NO;
84+
}
85+
86+
+ (BOOL)enableLegacyLoginItem {
87+
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
88+
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
89+
NSURL *applicationPathURL = [NSURL fileURLWithPath:applicationPath];
90+
if (sharedFileList) {
91+
LSSharedFileListItemRef sharedFileListItem = LSSharedFileListInsertItemURL(sharedFileList, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)applicationPathURL, NULL, NULL);
92+
if (sharedFileListItem) {
93+
CFRelease(sharedFileListItem);
94+
}
95+
CFRelease(sharedFileList);
96+
return YES;
97+
} else {
98+
NSLog(@"Unable to create the shared file list.");
99+
return NO;
100+
}
101+
}
102+
103+
+ (BOOL)disableLegacyLoginItem {
104+
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
105+
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
106+
if (sharedFileList) {
107+
UInt32 seedValue;
108+
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
109+
for (id sharedFile in sharedFileListArray) {
110+
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
111+
CFURLRef applicationPathURL;
112+
if (LSSharedFileListItemResolve(sharedFileListItem, 0, &applicationPathURL, NULL) == noErr) {
113+
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
114+
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
115+
LSSharedFileListItemRemove(sharedFileList, sharedFileListItem);
116+
}
117+
CFRelease(applicationPathURL);
118+
}
119+
}
120+
CFRelease(sharedFileList);
121+
return YES;
122+
} else {
123+
NSLog(@"Unable to create the shared file list.");
124+
return NO;
125+
}
126+
}
127+
128+
@end

src/main/native/org_cryptomator_macos_autostart_MacLaunchServices_Native.m

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,72 +7,16 @@
77
//
88

99
#import "org_cryptomator_macos_autostart_MacLaunchServices_Native.h"
10-
#import <Foundation/Foundation.h>
10+
#import "SKYLaunchService.h"
1111

1212
JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_isLoginItemEnabled(JNIEnv *env, jobject thisObj) {
13-
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
14-
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
15-
if (sharedFileList) {
16-
UInt32 seedValue;
17-
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
18-
for (id sharedFile in sharedFileListArray) {
19-
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
20-
CFURLRef applicationPathURL = NULL;
21-
LSSharedFileListItemResolve(sharedFileListItem, 0, (CFURLRef *)&applicationPathURL, NULL);
22-
if (applicationPathURL != NULL) {
23-
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
24-
CFRelease(applicationPathURL);
25-
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
26-
CFRelease(sharedFileList);
27-
return YES;
28-
}
29-
}
30-
}
31-
CFRelease(sharedFileList);
32-
} else {
33-
NSLog(@"Unable to create the shared file list.");
34-
}
35-
return NO;
13+
return [SKYLaunchService isLoginItemEnabled];
3614
}
3715

3816
JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_enableLoginItem(JNIEnv *env, jobject thisObj) {
39-
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
40-
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
41-
NSURL *applicationPathURL = [NSURL fileURLWithPath:applicationPath];
42-
if (sharedFileList) {
43-
LSSharedFileListItemRef sharedFileListItem = LSSharedFileListInsertItemURL(sharedFileList, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)applicationPathURL, NULL, NULL);
44-
if (sharedFileListItem) {
45-
CFRelease(sharedFileListItem);
46-
}
47-
CFRelease(sharedFileList);
48-
return YES;
49-
} else {
50-
NSLog(@"Unable to create the shared file list.");
51-
return NO;
52-
}
17+
return [SKYLaunchService enableLoginItem];
5318
}
5419

5520
JNIEXPORT jboolean JNICALL Java_org_cryptomator_macos_autostart_MacLaunchServices_00024Native_disableLoginItem(JNIEnv *env, jobject thisObj) {
56-
LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
57-
NSString *applicationPath = NSBundle.mainBundle.bundlePath;
58-
if (sharedFileList) {
59-
UInt32 seedValue;
60-
NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue));
61-
for (id sharedFile in sharedFileListArray) {
62-
LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile;
63-
CFURLRef applicationPathURL;
64-
if (LSSharedFileListItemResolve(sharedFileListItem, 0, &applicationPathURL, NULL) == noErr) {
65-
NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path];
66-
if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) {
67-
LSSharedFileListItemRemove(sharedFileList, sharedFileListItem);
68-
}
69-
CFRelease(applicationPathURL);
70-
}
71-
}
72-
CFRelease(sharedFileList);
73-
return YES;
74-
} else {
75-
NSLog(@"Unable to create the shared file list.");
76-
return NO;
77-
}
21+
return [SKYLaunchService disableLoginItem];
7822
}

0 commit comments

Comments
 (0)