Support UserNotifications framework (#3040)

This commit is contained in:
A Cœur 2022-11-24 03:47:56 +08:00 committed by GitHub
parent 19bc15523f
commit b612020576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 216 additions and 85 deletions

View File

@ -399,6 +399,7 @@
C809AEE7291ECFD000BFDBE1 /* NSDataAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = C809AEE6291ECFD000BFDBE1 /* NSDataAdditions.mm */; };
C841A28129197724009F18E8 /* NSKeyedUnarchiverAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = C841A28029197724009F18E8 /* NSKeyedUnarchiverAdditions.mm */; };
C86BCD9928228A9600F45599 /* SparkleProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = C86BCD9828228A9600F45599 /* SparkleProxy.mm */; };
C87369652809984200573C90 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C87369642809984200573C90 /* UserNotifications.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
C88771AD2803EE7B005C7523 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C88771A92803EE42005C7523 /* libz.tbd */; };
C88771AE2803EE7C005C7523 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C88771A92803EE42005C7523 /* libz.tbd */; };
C88771AF2803EE7D005C7523 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C88771A92803EE42005C7523 /* libz.tbd */; };
@ -1183,6 +1184,7 @@
C841A27F29197724009F18E8 /* NSKeyedUnarchiverAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSKeyedUnarchiverAdditions.h; sourceTree = "<group>"; };
C841A28029197724009F18E8 /* NSKeyedUnarchiverAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NSKeyedUnarchiverAdditions.mm; sourceTree = "<group>"; };
C86BCD9828228A9600F45599 /* SparkleProxy.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SparkleProxy.mm; sourceTree = "<group>"; };
C87369642809984200573C90 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
C88771A92803EE42005C7523 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
C88771AB2803EE53005C7523 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
C887BEC02807FCE900867D3C /* create.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = create.cc; sourceTree = "<group>"; };
@ -1239,6 +1241,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C87369652809984200573C90 /* UserNotifications.framework in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */,
4D1838DD09DEC0E80047D688 /* libtransmission.a in Frameworks */,
@ -1873,6 +1876,7 @@
A2F35BBA15C5A0A100EBF632 /* Frameworks */ = {
isa = PBXGroup;
children = (
C87369642809984200573C90 /* UserNotifications.framework */,
55869925257074EC00F77A43 /* libcurl.tbd */,
C88771AB2803EE53005C7523 /* libiconv.tbd */,
C88771A92803EE42005C7523 /* libz.tbd */,

View File

@ -444,6 +444,7 @@ target_link_libraries(${TR_NAME}-mac
"-framework IOKit"
"-framework Quartz"
"-framework Security"
"-weak_framework UserNotifications"
)
if(NOT CMAKE_GENERATOR STREQUAL Xcode)

View File

@ -5,6 +5,7 @@
@import IOKit;
@import IOKit.pwr_mgt;
@import Carbon;
@import UserNotifications;
@import Sparkle;
@ -232,7 +233,7 @@ static void removeKeRangerRansomware()
NSLog(@"OSX.KeRanger.A ransomware removal completed, proceeding to normal operation");
}
@interface Controller ()
@interface Controller ()<UNUserNotificationCenterDelegate>
@property(nonatomic) IBOutlet NSWindow* fWindow;
@property(nonatomic) IBOutlet NSStackView* fStackView;
@ -782,8 +783,6 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
self.fBadger = [[Badger alloc] initWithLib:self.fLib];
NSUserNotificationCenter.defaultUserNotificationCenter.delegate = self;
//observe notifications
NSNotificationCenter* nc = NSNotificationCenter.defaultCenter;
@ -842,6 +841,35 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
}
}
- (void)applicationWillFinishLaunching:(NSNotification*)notification
{
// user notifications
if (@available(macOS 10.14, *))
{
UNUserNotificationCenter.currentNotificationCenter.delegate = self;
UNNotificationAction* actionShow = [UNNotificationAction actionWithIdentifier:@"actionShow"
title:NSLocalizedString(@"Show", "notification button")
options:UNNotificationActionOptionForeground];
UNNotificationCategory* categoryShow = [UNNotificationCategory categoryWithIdentifier:@"categoryShow" actions:@[ actionShow ]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionNone];
[UNUserNotificationCenter.currentNotificationCenter setNotificationCategories:[NSSet setWithObject:categoryShow]];
[UNUserNotificationCenter.currentNotificationCenter
requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (error.code > 0)
{
NSLog(@"UserNotifications not configured: %@", error.localizedDescription);
}
}];
}
else
{
// Fallback on earlier versions
NSUserNotificationCenter.defaultUserNotificationCenter.delegate = self;
}
}
- (void)applicationDidFinishLaunching:(NSNotification*)notification
{
NSApp.servicesProvider = self;
@ -2338,11 +2366,37 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
self.fTotalTorrentsField.stringValue = totalTorrentsString;
}
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
willPresentNotification:(UNNotification*)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(macos(10.14))
{
completionHandler(-1);
}
- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center shouldPresentNotification:(NSUserNotification*)notification
{
return YES;
}
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
didReceiveNotificationResponse:(UNNotificationResponse*)response
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(macos(10.14))
{
if (!response.notification.request.content.userInfo.count)
{
return;
}
if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier])
{
[self didActivateNotificationByDefaultActionWithUserInfo:response.notification.request.content.userInfo];
}
else if ([response.actionIdentifier isEqualToString:@"actionShow"])
{
[self didActivateNotificationByActionShowWithUserInfo:response.notification.request.content.userInfo];
}
}
- (void)userNotificationCenter:(NSUserNotificationCenter*)center didActivateNotification:(NSUserNotification*)notification
{
if (!notification.userInfo)
@ -2352,29 +2406,67 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
if (notification.activationType == NSUserNotificationActivationTypeActionButtonClicked) //reveal
{
Torrent* torrent = [self torrentForHash:notification.userInfo[@"Hash"]];
NSString* location = torrent.dataLocation;
if (!location)
{
location = notification.userInfo[@"Location"];
}
if (location)
{
[NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:@[ [NSURL fileURLWithPath:location] ]];
}
[self didActivateNotificationByActionShowWithUserInfo:notification.userInfo];
}
else if (notification.activationType == NSUserNotificationActivationTypeContentsClicked)
{
Torrent* torrent = [self torrentForHash:notification.userInfo[@"Hash"]];
if (!torrent)
[self didActivateNotificationByDefaultActionWithUserInfo:notification.userInfo];
}
}
- (void)didActivateNotificationByActionShowWithUserInfo:(NSDictionary<NSString*, id>*)userInfo
{
Torrent* torrent = [self torrentForHash:userInfo[@"Hash"]];
NSString* location = torrent.dataLocation;
if (!location)
{
location = userInfo[@"Location"];
}
if (location)
{
[NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:@[ [NSURL fileURLWithPath:location] ]];
}
}
- (void)didActivateNotificationByDefaultActionWithUserInfo:(NSDictionary<NSString*, id>*)userInfo
{
Torrent* torrent = [self torrentForHash:userInfo[@"Hash"]];
if (!torrent)
{
return;
}
//select in the table - first see if it's already shown
NSInteger row = [self.fTableView rowForItem:torrent];
if (row == -1)
{
//if it's not shown, see if it's in a collapsed row
if ([self.fDefaults boolForKey:@"SortByGroup"])
{
return;
__block TorrentGroup* parent = nil;
[self.fDisplayedTorrents enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(TorrentGroup* group, NSUInteger idx, BOOL* stop) {
if ([group.torrents containsObject:torrent])
{
parent = group;
*stop = YES;
}
}];
if (parent)
{
[[self.fTableView animator] expandItem:parent];
row = [self.fTableView rowForItem:torrent];
}
}
//select in the table - first see if it's already shown
NSInteger row = [self.fTableView rowForItem:torrent];
if (row == -1)
{
//if it's not shown, see if it's in a collapsed row
//not found - must be filtering
NSAssert([self.fDefaults boolForKey:@"FilterBar"], @"expected the filter to be enabled");
[self.fFilterBar reset:YES];
row = [self.fTableView rowForItem:torrent];
//if it's not shown, it has to be in a collapsed row...again
if ([self.fDefaults boolForKey:@"SortByGroup"])
{
__block TorrentGroup* parent = nil;
@ -2392,41 +2484,13 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
row = [self.fTableView rowForItem:torrent];
}
}
if (row == -1)
{
//not found - must be filtering
NSAssert([self.fDefaults boolForKey:@"FilterBar"], @"expected the filter to be enabled");
[self.fFilterBar reset:YES];
row = [self.fTableView rowForItem:torrent];
//if it's not shown, it has to be in a collapsed row...again
if ([self.fDefaults boolForKey:@"SortByGroup"])
{
__block TorrentGroup* parent = nil;
[self.fDisplayedTorrents enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(TorrentGroup* group, NSUInteger idx, BOOL* stop) {
if ([group.torrents containsObject:torrent])
{
parent = group;
*stop = YES;
}
}];
if (parent)
{
[[self.fTableView animator] expandItem:parent];
row = [self.fTableView rowForItem:torrent];
}
}
}
}
NSAssert1(row != -1, @"expected a row to be found for torrent %@", torrent);
[self showMainWindow:nil];
[self.fTableView selectAndScrollToRow:row];
}
NSAssert1(row != -1, @"expected a row to be found for torrent %@", torrent);
[self showMainWindow:nil];
[self.fTableView selectAndScrollToRow:row];
}
- (Torrent*)torrentForHash:(NSString*)hash
@ -2461,24 +2525,39 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
}
}
NSString* title = NSLocalizedString(@"Download Complete", "notification title");
NSString* body = torrent.name;
NSString* location = torrent.dataLocation;
NSString* notificationTitle = NSLocalizedString(@"Download Complete", "notification title");
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = notificationTitle;
notification.informativeText = torrent.name;
notification.hasActionButton = YES;
notification.actionButtonTitle = NSLocalizedString(@"Show", "notification button");
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithObject:torrent.hashString forKey:@"Hash"];
if (location)
{
userInfo[@"Location"] = location;
}
notification.userInfo = userInfo;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
if (@available(macOS 10.14, *))
{
NSString* identifier = [@"Download Complete " stringByAppendingString:torrent.hashString];
UNMutableNotificationContent* content = [UNMutableNotificationContent new];
content.title = title;
content.body = body;
content.categoryIdentifier = @"categoryShow";
content.userInfo = userInfo;
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil];
[UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:request withCompletionHandler:nil];
}
else
{
// Fallback on earlier versions
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = title;
notification.informativeText = body;
notification.hasActionButton = YES;
notification.actionButtonTitle = NSLocalizedString(@"Show", "notification button");
notification.userInfo = userInfo;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
}
if (!self.fWindow.mainWindow)
{
@ -2513,24 +2592,39 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
}
}
NSString* title = NSLocalizedString(@"Seeding Complete", "notification title");
NSString* body = torrent.name;
NSString* location = torrent.dataLocation;
NSString* notificationTitle = NSLocalizedString(@"Seeding Complete", "notification title");
NSUserNotification* userNotification = [[NSUserNotification alloc] init];
userNotification.title = notificationTitle;
userNotification.informativeText = torrent.name;
userNotification.hasActionButton = YES;
userNotification.actionButtonTitle = NSLocalizedString(@"Show", "notification button");
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithObject:torrent.hashString forKey:@"Hash"];
if (location)
{
userInfo[@"Location"] = location;
}
userNotification.userInfo = userInfo;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:userNotification];
if (@available(macOS 10.14, *))
{
NSString* identifier = [@"Seeding Complete " stringByAppendingString:torrent.hashString];
UNMutableNotificationContent* content = [UNMutableNotificationContent new];
content.title = title;
content.body = body;
content.categoryIdentifier = @"categoryShow";
content.userInfo = userInfo;
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil];
[UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:request withCompletionHandler:nil];
}
else
{
// Fallback on earlier versions
NSUserNotification* userNotification = [[NSUserNotification alloc] init];
userNotification.title = title;
userNotification.informativeText = body;
userNotification.hasActionButton = YES;
userNotification.actionButtonTitle = NSLocalizedString(@"Show", "notification button");
userNotification.userInfo = userInfo;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:userNotification];
}
//removing from the list calls fullUpdateUI
if (torrent.removeWhenFinishSeeding)
@ -3364,13 +3458,30 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
if (![dict[@"ByUser"] boolValue])
{
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = isLimited ? NSLocalizedString(@"Speed Limit Auto Enabled", "notification title") :
NSLocalizedString(@"Speed Limit Auto Disabled", "notification title");
notification.informativeText = NSLocalizedString(@"Bandwidth settings changed", "notification description");
notification.hasActionButton = NO;
NSString* title = isLimited ? NSLocalizedString(@"Speed Limit Auto Enabled", "notification title") :
NSLocalizedString(@"Speed Limit Auto Disabled", "notification title");
NSString* body = NSLocalizedString(@"Bandwidth settings changed", "notification description");
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
if (@available(macOS 10.14, *))
{
NSString* identifier = @"Bandwidth settings changed";
UNMutableNotificationContent* content = [UNMutableNotificationContent new];
content.title = title;
content.body = body;
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil];
[UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:request withCompletionHandler:nil];
}
else
{
// Fallback on earlier versions
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = title;
notification.informativeText = body;
notification.hasActionButton = NO;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
}
}
}
@ -3468,12 +3579,27 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
[self openFiles:@[ fullFile ] addType:ADD_AUTO forcePath:nil];
NSString* notificationTitle = NSLocalizedString(@"Torrent File Auto Added", "notification title");
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = notificationTitle;
notification.informativeText = file;
notification.hasActionButton = NO;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
if (@available(macOS 10.14, *))
{
NSString* identifier = [@"Torrent File Auto Added " stringByAppendingString:file];
UNMutableNotificationContent* content = [UNMutableNotificationContent new];
content.title = notificationTitle;
content.body = file;
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:nil];
[UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:request withCompletionHandler:nil];
}
else
{
// Fallback on earlier versions
NSUserNotification* notification = [[NSUserNotification alloc] init];
notification.title = notificationTitle;
notification.informativeText = file;
notification.hasActionButton = NO;
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
}
}
}