From e79b42baa35f5785e270316bfbb3d2c2444149cf Mon Sep 17 00:00:00 2001 From: rich2014 Date: Tue, 9 Jul 2024 22:30:40 +0800 Subject: [PATCH 1/4] Cocoa: add definitions related to UNUserNotification --- lcl/interfaces/cocoa/cocoa_extra.pas | 61 ++++++++++++++++++++++++++++ lcl/interfaces/lcl.lpk | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/lcl/interfaces/cocoa/cocoa_extra.pas b/lcl/interfaces/cocoa/cocoa_extra.pas index ed7e2559bd..1179f3b2a6 100644 --- a/lcl/interfaces/cocoa/cocoa_extra.pas +++ b/lcl/interfaces/cocoa/cocoa_extra.pas @@ -56,7 +56,68 @@ var NSPasteboardTypeRuler: NSPasteboardType; cvar; external; NSPasteboardTypeSound: NSPasteboardType; cvar; external; +const + UNAuthorizationOptionBadge = 1 shl 0; + UNAuthorizationOptionSound = 1 shl 1; + UNAuthorizationOptionAlert = 1 shl 2; + UNAuthorizationOptionCarPlay = 1 shl 3; + type + UNNotificationContent = objcclass external (NSObject) + public + function title: NSString; message 'title'; + function subtitle: NSString; message 'subtitle'; + function body: NSString; message 'body'; + function badge: NSNumber; message 'badge'; + end; + + UNMutableNotificationContent = objcclass external (UNNotificationContent) + public + procedure setTitle( newValue: NSString ); message 'setTitle:'; + procedure setSubtitle( newValue: NSString ); message 'setSubtitle:'; + procedure setBody( newValue: NSString ); message 'setBody:'; + procedure setBadge( newValue: NSNumber); message 'setBadge:'; + end; + + UNNotificationTrigger = objcclass external (NSObject) + public + function repeats: ObjCBool; message 'repeats'; + end; + + UNTimeIntervalNotificationTrigger = objcclass external (UNNotificationTrigger) + public + class function triggerWithTimeInterval_repeats( + timeInterval: NSTimeInterval; repeats_: ObjCBool ): id; + message 'triggerWithTimeInterval:repeats:'; + end; + + UNNotificationRequest = objcclass external (NSObject) + public + class function requestWithIdentifier_content_trigger( + identifier: NSString; + content: UNNotificationContent; + trigger: UNNotificationTrigger ): id; + message 'requestWithIdentifier:content:trigger:'; + function identifier: NSString; message 'identifier'; + function content: UNNotificationContent; message 'content'; + function trigger: UNNotificationTrigger; message 'trigger'; + end; + + UNAuthorizationOptions = NSUInteger; + + UNUserNotificationCenter = objcclass external (NSObject) + public + class function currentNotificationCenter: UNUserNotificationCenter; + message 'currentNotificationCenter'; + procedure requestAuthorizationWithOptions_completionHandler( + options: UNAuthorizationOptions; completionHandler: OpaqueCBlock = nil ); + message 'requestAuthorizationWithOptions:completionHandler:'; + procedure addNotificationRequest_withCompletionHandler( + request: UNNotificationRequest; + completionHandler: OpaqueCBlock = nil ); + message 'addNotificationRequest:withCompletionHandler:'; + end; + NSMenuFix = objccategory external (NSMenu) function itemAtIndex(index: NSInteger): NSMenuItem; message 'itemAtIndex:'; end; diff --git a/lcl/interfaces/lcl.lpk b/lcl/interfaces/lcl.lpk index edf47b68e9..b978863a0f 100644 --- a/lcl/interfaces/lcl.lpk +++ b/lcl/interfaces/lcl.lpk @@ -83,7 +83,7 @@ if TargetOS='darwin' then begin +' -framework OpenGL' +' ''-dylib_file'' ''/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib'''; end else if LCLWidgetType='cocoa' then - UsageLinkerOptions := '-framework Cocoa'; + UsageLinkerOptions := '-framework Cocoa -framework UserNotifications'; end else if TargetOS='solaris' then begin UsageLibraryPath:='/usr/X11R6/lib'; end;"/> From 14d9977fc8036951cdc820f31a9752580ba41080 Mon Sep 17 00:00:00 2001 From: rich2014 Date: Tue, 9 Jul 2024 22:31:06 +0800 Subject: [PATCH 2/4] Cocoa: apply Notification to ShowBalloonHint (New and Legacy) --- lcl/interfaces/cocoa/cocoatrayicon.inc | 46 ++++++++++++++++++++++-- lcl/interfaces/cocoa/cocoawsextctrls.pas | 3 ++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoatrayicon.inc b/lcl/interfaces/cocoa/cocoatrayicon.inc index 1af900bf7c..90708e563b 100644 --- a/lcl/interfaces/cocoa/cocoatrayicon.inc +++ b/lcl/interfaces/cocoa/cocoatrayicon.inc @@ -154,13 +154,47 @@ begin StatusItemHandle.lclSetTrayIcon(ATrayIcon); end; -class function TCocoaWSCustomTrayIcon.ShowBalloonHint(const ATrayIcon: TCustomTrayIcon): Boolean; +// macOS 10.14+ required +// APP codesign required +// codesign --deep --force --verify --verbose --sign '-' 'your.app' +class function TCocoaWSCustomTrayIcon.newUserNotify(const ATrayIcon: TCustomTrayIcon): Boolean; +var + nc: UNUserNotificationCenter; + trigger: UNTimeIntervalNotificationTrigger; + message: UNMutableNotificationContent; + request: UNNotificationRequest; +const + options: NSInteger = UNAuthorizationOptionAlert or + UNAuthorizationOptionBadge or + UNAuthorizationOptionSound; +begin + Result:= True; + + nc:= UNUserNotificationCenter.currentNotificationCenter; + nc.requestAuthorizationWithOptions_completionHandler( options ); + + trigger:= UNTimeIntervalNotificationTrigger.triggerWithTimeInterval_repeats( + 0.01, False); + + message:= UNMutableNotificationContent.new; + message.setTitle( StrToNSString(ATrayIcon.BalloonTitle) ); + message.setBody( StrToNSString(ATrayIcon.BalloonHint) ); + + request:= UNNotificationRequest.requestWithIdentifier_content_trigger( + NSString.string_, message, trigger ); + + nc.addNotificationRequest_withCompletionHandler( request ); + + message.release; +end; + +class function TCocoaWSCustomTrayIcon.legacyUserNotify(const ATrayIcon: TCustomTrayIcon): Boolean; var nc: NSUserNotificationCenter; message: NSUserNotification; begin Result := True; - message:= NSUserNotification.alloc.init; + message:= NSUserNotification.new; message.setTitle( StrToNSString(ATrayIcon.BalloonTitle) ); message.setInformativeText( StrToNSString(ATrayIcon.BalloonHint) ); @@ -174,6 +208,14 @@ begin message.release; end; +class function TCocoaWSCustomTrayIcon.ShowBalloonHint(const ATrayIcon: TCustomTrayIcon): Boolean; +begin + if NSAppKitVersionNumber >= NSAppKitVersionNumber11_0 then + Result:= newUserNotify( ATrayIcon ) // APP codesign required + else + Result:= legacyUserNotify( ATrayIcon ); +end; + class function TCocoaWSCustomTrayIcon.GetPosition(const ATrayIcon: TCustomTrayIcon): TPoint; begin Result := Point(0, 0); diff --git a/lcl/interfaces/cocoa/cocoawsextctrls.pas b/lcl/interfaces/cocoa/cocoawsextctrls.pas index 3bbbd10971..361db6acd1 100644 --- a/lcl/interfaces/cocoa/cocoawsextctrls.pas +++ b/lcl/interfaces/cocoa/cocoawsextctrls.pas @@ -174,6 +174,9 @@ type { TCocoaWSCustomTrayIcon } TCocoaWSCustomTrayIcon = class(TWSCustomTrayIcon) + private + class function newUserNotify(const ATrayIcon: TCustomTrayIcon): Boolean; + class function legacyUserNotify(const ATrayIcon: TCustomTrayIcon): Boolean; published class function Hide(const ATrayIcon: TCustomTrayIcon): Boolean; override; class function Show(const ATrayIcon: TCustomTrayIcon): Boolean; override; From caad04b08d4110d27e9b2ae14cd917d23ee2434f Mon Sep 17 00:00:00 2001 From: rich2014 Date: Tue, 9 Jul 2024 22:32:48 +0800 Subject: [PATCH 3/4] Cocoa: add system default Sound to notification --- lcl/interfaces/cocoa/cocoa_extra.pas | 8 +++++++- lcl/interfaces/cocoa/cocoatrayicon.inc | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lcl/interfaces/cocoa/cocoa_extra.pas b/lcl/interfaces/cocoa/cocoa_extra.pas index 1179f3b2a6..f72787787e 100644 --- a/lcl/interfaces/cocoa/cocoa_extra.pas +++ b/lcl/interfaces/cocoa/cocoa_extra.pas @@ -63,12 +63,17 @@ const UNAuthorizationOptionCarPlay = 1 shl 3; type + UNNotificationSound = objcclass external (NSObject) + class function defaultSound: UNNotificationSound; message 'defaultSound'; + end; + UNNotificationContent = objcclass external (NSObject) public function title: NSString; message 'title'; function subtitle: NSString; message 'subtitle'; function body: NSString; message 'body'; function badge: NSNumber; message 'badge'; + function sound: UNNotificationSound; message 'sound'; end; UNMutableNotificationContent = objcclass external (UNNotificationContent) @@ -76,7 +81,8 @@ type procedure setTitle( newValue: NSString ); message 'setTitle:'; procedure setSubtitle( newValue: NSString ); message 'setSubtitle:'; procedure setBody( newValue: NSString ); message 'setBody:'; - procedure setBadge( newValue: NSNumber); message 'setBadge:'; + procedure setBadge( newValue: NSNumber ); message 'setBadge:'; + procedure setSound( newValue: UNNotificationSound ); message 'setSound:'; end; UNNotificationTrigger = objcclass external (NSObject) diff --git a/lcl/interfaces/cocoa/cocoatrayicon.inc b/lcl/interfaces/cocoa/cocoatrayicon.inc index 90708e563b..e072b566dc 100644 --- a/lcl/interfaces/cocoa/cocoatrayicon.inc +++ b/lcl/interfaces/cocoa/cocoatrayicon.inc @@ -179,6 +179,7 @@ begin message:= UNMutableNotificationContent.new; message.setTitle( StrToNSString(ATrayIcon.BalloonTitle) ); message.setBody( StrToNSString(ATrayIcon.BalloonHint) ); + message.setSound( UNNotificationSound.defaultSound ); request:= UNNotificationRequest.requestWithIdentifier_content_trigger( NSString.string_, message, trigger ); @@ -197,6 +198,8 @@ begin message:= NSUserNotification.new; message.setTitle( StrToNSString(ATrayIcon.BalloonTitle) ); message.setInformativeText( StrToNSString(ATrayIcon.BalloonHint) ); + if NOT NSApplication(NSApp).isActive then + message.setSoundName( NSUserNotificationDefaultSoundName ); nc:= NSUserNotificationCenter.defaultUserNotificationCenter; if CocoaConfig.CocoaAlwaysPresentNotification then begin From 2fa8c3206c2376a73711bb38e8a3818ca591cb18 Mon Sep 17 00:00:00 2001 From: rich2014 Date: Tue, 9 Jul 2024 22:33:07 +0800 Subject: [PATCH 4/4] Cocoa: fix the linker issue that APP could not run on macOS 10.13- after linking the UserNotifications Framework --- lcl/interfaces/lcl.lpk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lcl/interfaces/lcl.lpk b/lcl/interfaces/lcl.lpk index b978863a0f..9f7ba5c734 100644 --- a/lcl/interfaces/lcl.lpk +++ b/lcl/interfaces/lcl.lpk @@ -83,7 +83,7 @@ if TargetOS='darwin' then begin +' -framework OpenGL' +' ''-dylib_file'' ''/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib'''; end else if LCLWidgetType='cocoa' then - UsageLinkerOptions := '-framework Cocoa -framework UserNotifications'; + UsageLinkerOptions := '-framework Cocoa -weak_framework UserNotifications'; end else if TargetOS='solaris' then begin UsageLibraryPath:='/usr/X11R6/lib'; end;"/>