Skip to content

Commit 719203a

Browse files
authored
Merge pull request #1501 from hiddenSharp429/develop
Feat(QRCode): Enhance 'Scan QR Code From Screen' notifications # #1250
2 parents 4c731d3 + 3fccede commit 719203a

File tree

5 files changed

+144
-55
lines changed

5 files changed

+144
-55
lines changed

.github/workflows/feature.yml

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jobs:
2424
- name: Build
2525
run: |
2626
brew install automake
27+
brew install autoconf
28+
brew install libtool
2729
make VERSION="${GITHUB_SHA::7}" debug
2830
make debug-dmg
2931
shasum -a 256 build/Debug/ShadowsocksX-NG.dmg > build/Debug/ShadowsocksX-NG.dmg.checksum

.github/workflows/release.yml

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ jobs:
2121
- name: Build
2222
run: |
2323
brew install automake
24+
brew install autoconf
25+
brew install libtool
2426
make VERSION="${GITHUB_REF_NAME}" release
2527
make release-dmg
2628
shasum -a 256 build/Release/ShadowsocksX-NG.dmg > build/Release/ShadowsocksX-NG.dmg.checksum

ShadowsocksX-NG/AppDelegate.swift

+24-23
Original file line numberDiff line numberDiff line change
@@ -627,42 +627,43 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
627627
}
628628

629629
func handleFoundSSURL(_ note: Notification) {
630-
let sendNotify = {
631-
(title: String, subtitle: String, infoText: String) in
632-
630+
let sendNotify = { (title: String, subtitle: String, infoText: String) in
633631
let userNote = NSUserNotification()
634632
userNote.title = title
635633
userNote.subtitle = subtitle
636634
userNote.informativeText = infoText
637635
userNote.soundName = NSUserNotificationDefaultSoundName
638636

639-
NSUserNotificationCenter.default
640-
.deliver(userNote);
637+
NSUserNotificationCenter.default.deliver(userNote)
641638
}
642639

643640
if let userInfo = (note as NSNotification).userInfo {
644-
let urls: [URL] = userInfo["urls"] as! [URL]
641+
// 检查错误
642+
if let error = userInfo["error"] as? String {
643+
sendNotify("Scan Failed", "", error.localized)
644+
return
645+
}
646+
647+
// 使用新的通知信息
648+
let title = (userInfo["title"] as? String) ?? ""
649+
let subtitle = (userInfo["subtitle"] as? String) ?? ""
650+
let body = (userInfo["body"] as? String) ?? ""
645651

646-
let mgr = ServerProfileManager.instance
647-
let addCount = mgr.addServerProfileByURL(urls: urls)
652+
let urls: [URL] = userInfo["urls"] as! [URL]
653+
let addCount = ServerProfileManager.instance.addServerProfileByURL(urls: urls)
648654

649655
if addCount > 0 {
650-
var subtitle: String = ""
651-
if userInfo["source"] as! String == "qrcode" {
652-
subtitle = "By scan QR Code".localized
653-
} else if userInfo["source"] as! String == "url" {
654-
subtitle = "By handle SS URL".localized
655-
} else if userInfo["source"] as! String == "pasteboard" {
656-
subtitle = "By import from pasteboard".localized
657-
}
658-
659-
sendNotify("Add \(addCount) Shadowsocks Server Profile".localized, subtitle, "")
656+
sendNotify(
657+
title.localized,
658+
subtitle.localized,
659+
"Successfully added \(addCount) server configuration(s)".localized
660+
)
660661
} else {
661-
if userInfo["source"] as! String == "qrcode" {
662-
sendNotify("", "", "Not found valid QRCode of shadowsocks profile".localized)
663-
} else if userInfo["source"] as! String == "url" {
664-
sendNotify("", "", "Not found valid URL of shadowsocks profile".localized)
665-
}
662+
sendNotify(
663+
title.localized,
664+
subtitle.localized,
665+
body.localized
666+
)
666667
}
667668
}
668669
}

ShadowsocksX-NG/Info.plist

+2
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,7 @@
4949
<string>MainMenu</string>
5050
<key>NSPrincipalClass</key>
5151
<string>SWBApplication</string>
52+
<key>NSScreenCaptureUsageDescription</key>
53+
<string>ShadowsocksX-NG needs Screen Recording permission to scan QR codes on your screen</string>
5254
</dict>
5355
</plist>

ShadowsocksX-NG/Utils.m

+114-32
Original file line numberDiff line numberDiff line change
@@ -10,71 +10,153 @@
1010
#import <CoreImage/CoreImage.h>
1111
#import <AppKit/AppKit.h>
1212

13-
void ScanQRCodeOnScreen(void) {
13+
void ScanQRCodeOnScreen(void) {
14+
/* check system version and permission status */
15+
if (@available(macOS 10.12, *)) {
16+
BOOL hasPermission = CGPreflightScreenCaptureAccess();
17+
NSLog(@"Screen Recording Permission Status: %@", hasPermission ? @"Granted" : @"Not Granted");
18+
19+
if (!hasPermission) {
20+
NSLog(@"Requesting Screen Recording Permission...");
21+
CGRequestScreenCaptureAccess();
22+
23+
/* check permission status after request */
24+
hasPermission = CGPreflightScreenCaptureAccess();
25+
NSLog(@"Screen Recording Permission Status After Request: %@", hasPermission ? @"Granted" : @"Not Granted");
26+
27+
if (!hasPermission) {
28+
NSLog(@"Screen Recording Permission Denied");
29+
30+
/* send notification about permission missing */
31+
[[NSNotificationCenter defaultCenter]
32+
postNotificationName:@"NOTIFY_FOUND_SS_URL"
33+
object:nil
34+
userInfo:@{
35+
@"urls": @[],
36+
@"source": @"qrcode",
37+
@"error": @"Screen Recording permission required. Please grant permission in System Preferences and restart ShadowsocksX-NG"
38+
}];
39+
40+
/* open system privacy settings */
41+
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"]];
42+
return;
43+
}
44+
}
45+
46+
NSLog(@"Proceeding with screen capture...");
47+
}
48+
1449
/* displays[] Quartz display ID's */
1550
CGDirectDisplayID *displays = nil;
16-
17-
CGError err = CGDisplayNoErr;
1851
CGDisplayCount dspCount = 0;
1952

20-
/* How many active displays do we have? */
21-
err = CGGetActiveDisplayList(0, NULL, &dspCount);
53+
/* variables for collecting scan information */
54+
NSMutableDictionary *scanInfo = [NSMutableDictionary dictionary];
55+
NSMutableArray *foundSSUrls = [NSMutableArray array];
56+
NSMutableArray *foundQRCodes = [NSMutableArray array];
2257

23-
/* If we are getting an error here then their won't be much to display. */
24-
if(err != CGDisplayNoErr)
25-
{
26-
NSLog(@"Could not get active display count (%d)\n", err);
58+
/* How many active displays do we have? */
59+
CGError err = CGGetActiveDisplayList(0, NULL, &dspCount);
60+
61+
if(err != CGDisplayNoErr) {
62+
[[NSNotificationCenter defaultCenter]
63+
postNotificationName:@"NOTIFY_FOUND_SS_URL"
64+
object:nil
65+
userInfo:@{
66+
@"urls": @[],
67+
@"source": @"qrcode",
68+
@"error": @"Failed to get display list"
69+
}];
2770
return;
2871
}
2972

73+
scanInfo[@"displayCount"] = @(dspCount);
74+
NSLog(@"Found %d displays", dspCount);
75+
3076
/* Allocate enough memory to hold all the display IDs we have. */
3177
displays = calloc((size_t)dspCount, sizeof(CGDirectDisplayID));
3278

3379
// Get the list of active displays
34-
err = CGGetActiveDisplayList(dspCount,
35-
displays,
36-
&dspCount);
37-
38-
/* More error-checking here. */
39-
if(err != CGDisplayNoErr)
40-
{
41-
NSLog(@"Could not get active display list (%d)\n", err);
80+
err = CGGetActiveDisplayList(dspCount, displays, &dspCount);
81+
82+
if(err != CGDisplayNoErr) {
83+
free(displays);
84+
[[NSNotificationCenter defaultCenter]
85+
postNotificationName:@"NOTIFY_FOUND_SS_URL"
86+
object:nil
87+
userInfo:@{
88+
@"urls": @[],
89+
@"source": @"qrcode",
90+
@"error": @"Failed to get display information"
91+
}];
4292
return;
4393
}
4494

45-
NSMutableArray* foundSSUrls = [NSMutableArray array];
46-
4795
CIDetector *detector = [CIDetector detectorOfType:@"CIDetectorTypeQRCode"
48-
context:nil
49-
options:@{ CIDetectorAccuracy:CIDetectorAccuracyHigh }];
96+
context:nil
97+
options:@{ CIDetectorAccuracy:CIDetectorAccuracyHigh }];
5098

51-
for (unsigned int displaysIndex = 0; displaysIndex < dspCount; displaysIndex++)
52-
{
53-
/* Make a snapshot image of the current display. */
99+
int totalQRCodesFound = 0;
100+
int validSSUrlsFound = 0;
101+
102+
for (unsigned int displaysIndex = 0; displaysIndex < dspCount; displaysIndex++) {
54103
CGImageRef image = CGDisplayCreateImage(displays[displaysIndex]);
55104
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image]];
105+
106+
/* count total QR codes found */
107+
totalQRCodesFound += (int)features.count;
108+
56109
for (CIQRCodeFeature *feature in features) {
57-
NSLog(@"%@", feature.messageString);
58-
if ( [feature.messageString hasPrefix:@"ss://"] )
59-
{
110+
NSLog(@"Found QR Code: %@", feature.messageString);
111+
[foundQRCodes addObject:feature.messageString];
112+
113+
if ([feature.messageString hasPrefix:@"ss://"]) {
60114
NSURL *url = [NSURL URLWithString:feature.messageString];
61115
if (url) {
62116
[foundSSUrls addObject:url];
117+
validSSUrlsFound++;
63118
}
64119
}
65120
}
66-
CGImageRelease(image);
121+
CGImageRelease(image);
67122
}
68123

69124
free(displays);
70125

126+
/* prepare notification information */
127+
NSString *notificationTitle;
128+
NSString *notificationSubtitle;
129+
NSString *notificationBody;
130+
131+
if (totalQRCodesFound == 0) {
132+
notificationTitle = [NSString stringWithFormat:@"Scanned %d displays", dspCount];
133+
notificationSubtitle = @"No QR codes found";
134+
notificationBody = @"Try adjusting the QR code position on your screen";
135+
} else if (validSSUrlsFound == 0) {
136+
notificationTitle = [NSString stringWithFormat:@"Found %d QR code(s)", totalQRCodesFound];
137+
notificationSubtitle = @"No valid Shadowsocks URLs";
138+
notificationBody = @"QR codes found are not Shadowsocks configuration";
139+
} else {
140+
notificationTitle = [NSString stringWithFormat:@"Found %d Shadowsocks URL(s)", validSSUrlsFound];
141+
notificationSubtitle = [NSString stringWithFormat:@"Scanned %d displays, found %d QR codes", dspCount, totalQRCodesFound];
142+
notificationBody = @"Processing Shadowsocks configuration...";
143+
}
144+
71145
[[NSNotificationCenter defaultCenter]
72146
postNotificationName:@"NOTIFY_FOUND_SS_URL"
73147
object:nil
74-
userInfo: @{ @"urls": foundSSUrls,
75-
@"source": @"qrcode"
76-
}
77-
];
148+
userInfo:@{
149+
@"urls": foundSSUrls,
150+
@"source": @"qrcode",
151+
@"title": notificationTitle,
152+
@"subtitle": notificationSubtitle,
153+
@"body": notificationBody,
154+
@"scanInfo": @{
155+
@"displayCount": @(dspCount),
156+
@"totalQRCodes": @(totalQRCodesFound),
157+
@"validURLs": @(validSSUrlsFound)
158+
}
159+
}];
78160
}
79161

80162
NSImage* createQRImage(NSString *string, NSSize size) {

0 commit comments

Comments
 (0)